//-------------------------------------------------------------------------------------------------
// <copyright file="Form1.cs" company="Microsoft">
//    Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// 
// <summary>
// Main form for DUAScriptGen.
// </summary>
//-------------------------------------------------------------------------------------------------

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using Microsoft.Win32;
using System.Diagnostics;

namespace DUAScriptGen
{
	enum E_FILEOPERATION { Move, Copy, Delete, Execute, MoveAndExecute, CopyAndExecute };
	enum E_REGISTRYOPERATION { Create, CreateAndAddValue, DeleteKey, DeleteValue, RestoreKey, SaveKey };
	enum E_REGISTRYROOT { HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_CURRENT_CONFIG};
	enum E_REGVALUEDATATYPE { REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_LINK, REG_MULTI_SZ, REG_RESOURCE_LIST, REG_QWORD, REG_QWORD_LITTLE_ENDIAN, REG_NONE};
	enum E_REGPARSEOPTION { Add, Remove };
	enum E_RELNOTE_SECTION { None, FileAdd, RegistryAdd, RegistryRemove };

	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		private System.Windows.Forms.ListView listView1;
		private System.Windows.Forms.GroupBox groupBox1;
		private System.Windows.Forms.ColumnHeader Filename;
		private System.Windows.Forms.GroupBox groupBox2;
		private System.Windows.Forms.Button AddFilesButton;
		private System.Windows.Forms.Button button1;
		private System.Windows.Forms.Button button4;
		private System.Windows.Forms.Button AddRegistryKey;
		private System.Windows.Forms.Button RemoveRegistryKey;
		private System.Windows.Forms.Button EditItem;
		private System.Windows.Forms.Button ImportRegKey;
		private System.Windows.Forms.OpenFileDialog openFileDialog1;
		private System.Windows.Forms.ListView listView2;
		private System.Windows.Forms.ColumnHeader TopLevelKey;
		private System.Windows.Forms.ColumnHeader Key;
		private System.Windows.Forms.SaveFileDialog saveFileDialog1;
		private System.Windows.Forms.MainMenu mainMenu1;
		private System.Windows.Forms.MenuItem menuItem1;
		private System.Windows.Forms.MenuItem menuItem3;
		private System.Windows.Forms.MenuItem Tools_Options;
		private System.Windows.Forms.MenuItem File_Exit;
		private System.Windows.Forms.Button ExecuteCompiler;

		public int g_iUseHTTPPolling;
		public int g_iRebootAfterScript;
		public string g_strDUAServerPath;
		public string g_strLocalDrive;
		public string g_strNextScriptName;
		public string g_strInf2RtfPath;

		public string g_strSavePath;
		public string g_strSaveFile;
		public string strSourceFile;
		public string DUA_OutputFolder;

		private System.Windows.Forms.MenuItem Help_About;
		private System.Windows.Forms.ColumnHeader Action;
		private System.Windows.Forms.Button EditRegistryKey;
		private System.Windows.Forms.ColumnHeader RegistryAction;

		// The order of these values should match the enumeration orders
		public string [] g_strFileDefaultLocations = { "\\windows", "\\windows\\system32", "\\windows\\system32\\drivers", "\\windows\\system32\\viewers", "\\windows\\system32\\spool\\drivers\\color",
														 "\\windows\\inf", "\\windows\\help", "\\windows\\fonts", "\\program files", "\\documents and settings\\all users\\start menu",
														 "\\documents and settings\\all users\\start menu\\programs", "\\documents and settings\\all users\\start menu\\programs\\startup",
														 "\\documents and settings\\all users\\desktop", "\\documents and settings\\all users\\favorites" };

		public string [] g_strFileOperations = {"Move", "Copy", "Delete", "Execute", "Move and execute", "Copy and execute"};
		public string [] g_strRegistryOperations = {"Create key", "Create key and add value", "Delete key", "Delete value", "Restore key", "Save key"};
		public string [] g_strRegRoots = { "HKEY_CLASSES_ROOT", "HKEY_CURRENT_USER", "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_PERFORMANCE_DATA", "HKEY_CURRENT_CONFIG"};
		public string [] g_strRegRootsShort = { "HKCR", "HKCU", "HKLM", "HKU", "HKPD", "HKCC"};
		public string [] g_strRegValueDataTypes = { "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", "REG_DWORD_BIG_ENDIAN", "REG_LINK", "REG_MULTI_SZ", "REG_RESOURCE_LIST", "REG_QWORD", "REG_QWORD_LITTLE_ENDIAN"};

		// These strings are tokens recognized by INF format and DUA format
		// The order of values should match in the 2 arrays
		public string [] g_strFolderTokens = {"%10%","%11%","%12%","%17%","%18%","%20%","%21%","%23%",
												 "%24%","%25%","%30%","%50%","%51%","%52%","%53%","%54%",
												 "%55%","%16406%","%16407%","%16408%","%16409%","%16414%",
												 "%16415%","%16419%","%16420%","%16421%","%16422%","%16425%",
												 "%16426%","%16427%","%16428%","%16429%","%16430%","%16431%",
												 "%16437%","%16438%","%16439%","%19999%","%49600%","%49800%",
												 "%49930%","%66000%","%66001%","%66002%","%66003%","%66004%"};

		public string [] g_strFolderTokenReplacements = {"\\windows",
															"\\windows\\system32",
															"\\windows\\system32\\drivers",
															"\\windows\\inf",
															"\\windows\\Help",
															"\\windows\\Fonts",
															"\\windows\\system32\\viewers",
															"\\windows\\system32\\spool\\drivers\\color",
															"",
															"\\windows",
															"",
															"\\windows\\system",
															"\\windows\\system32\\spool",
															"\\windows\\system32\\spool\\drivers\\w32x86",
															"\\Documents and Settings\\Default User",
															"",
															"\\windows\\system32\\spool\\prtprocs\\w32x86",
															"\\Documents and Settings\\All Users\\Start Menu",
															"\\Documents and Settings\\All Users\\Start Menu\\Programs",
															"\\Documents and Settings\\All Users\\Start Menu\\Programs\\Startup",
															"\\Documents and Settings\\All Users\\Desktop",
															"\\Documents and Settings\\All Users\\Start Menu\\Programs\\Startup",
															"\\Documents and Settings\\All Users\\Favorites",
															"\\Documents and Settings\\All Users\\Application Data",
															"\\windows",
															"\\windows\\system32",
															"\\Program Files",
															"\\windows\\system32",
															"\\Program Files",
															"\\Program Files\\Common Files",
															"\\Program Files\\Common Files",
															"\\Documents and Settings\\All Users\\Templates",
															"\\Documents and Settings\\All Users\\Documents",
															"\\Documents and Settings\\All Users\\Start Menu\\Programs\\Administrative Tools",
															"\\Documents and Settings\\All Users\\Documents\\My Music",
															"\\Documents and Settings\\All Users\\Documents\\My Pictures",
															"\\Documents and Settings\\All Users\\Documents\\My Video",
															"\\Documents and Settings",
															"\\Program Files",
															"\\Program Files\\Common Files",
															"\\Program Files\\Common Files\\Microsoft Shared",
															"\\windows\\i386",
															"\\windows\\i386",
															"\\windows\\i386",
															"\\windows\\i386",
															"\\windows\\i386"};

		// Version string to write to the end of the DUS file and show in Help | About
		// NOTE: This should be updated for every public refresh of DUAScriptGen so we will
		// know which version a customer has and is trying to use
		public string g_strVersionString = "v1.1.0011 (February 7, 2008)";

		// Strings to use when writing a DUS file
		public string g_strDuaScriptGenHeader = "// DUAScriptGen V1.1 Script File";
		public string g_strDuaScriptGenHeaderShort = "// DUAScriptGen ";
		public string g_strCopyright = "// (C) Microsoft Corporation";
		public string g_strFileSectionBegin = "// **** FILE OPERATIONS BEGIN ***";
		public string g_strFileSectionEnd = "// **** FILE OPERATIONS END ***";
		public string g_strRegistrySectionBegin = "// **** REGISTRY OPERATIONS BEGIN ***";
		public string g_strRegistrySectionEnd = "// **** REGISTRY OPERATIONS END ***";
		public string g_strPostRebootExecuteSectionBegin = "// **** POST-REBOOT OPERATIONS BEGIN ***";
		public string g_strPostRebootExecuteSectionEnd = "// **** POST-REBOOT OPERATIONS END ***";
		public string g_strFileInUseRenameExtension = ".old_duascriptgen";

		public string g_strUsageInfo = "DUAScriptGen supports creating DUS files silently. The following command\nline parameters are required for this functionality:\n\n/i <input filename> - full path of an RTF, HTML, or INF file to parse\n/o <output filename> - full path of the output DUS file\n\nExample: DUAScriptGen.exe /i c:\\input_file.inf /o c:\\output_file.dus";

		private System.Windows.Forms.ColumnHeader RegValue;
		private System.Windows.Forms.Button btnParseRTF;
		private System.Windows.Forms.RichTextBox rtxtQFEInfo;
		private System.Windows.Forms.ColumnHeader DestinationFolder;
		private System.Windows.Forms.MenuItem menuLoadDUSFile;
		private System.Windows.Forms.MenuItem menuItem4;
		private System.Windows.Forms.MenuItem menuClearAllFiles;
		private System.Windows.Forms.MenuItem menuClearAllRegistry;
		private System.Windows.Forms.MenuItem menuClearAll;
		private System.Windows.Forms.MenuItem menuItem2;
		private System.Windows.Forms.MenuItem menuGenerateScript;
		private System.Windows.Forms.MenuItem menuExecuteCompiler;
		private System.Windows.Forms.MenuItem menuItem5;
		private System.Windows.Forms.MenuItem menuConvertQFE;
		private System.Windows.Forms.Button btnParseInf;
		private System.Windows.Forms.MenuItem menuConvertInf;

		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			// Set default values for global variables
			g_iUseHTTPPolling = 1;
			g_iRebootAfterScript = 1;
			g_strDUAServerPath = "";
			g_strLocalDrive = "";
			g_strNextScriptName = "";
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.listView1 = new System.Windows.Forms.ListView();
			this.Filename = new System.Windows.Forms.ColumnHeader();
			this.Action = new System.Windows.Forms.ColumnHeader();
			this.DestinationFolder = new System.Windows.Forms.ColumnHeader();
			this.groupBox1 = new System.Windows.Forms.GroupBox();
			this.EditItem = new System.Windows.Forms.Button();
			this.button1 = new System.Windows.Forms.Button();
			this.AddFilesButton = new System.Windows.Forms.Button();
			this.groupBox2 = new System.Windows.Forms.GroupBox();
			this.listView2 = new System.Windows.Forms.ListView();
			this.TopLevelKey = new System.Windows.Forms.ColumnHeader();
			this.Key = new System.Windows.Forms.ColumnHeader();
			this.RegValue = new System.Windows.Forms.ColumnHeader();
			this.RegistryAction = new System.Windows.Forms.ColumnHeader();
			this.ImportRegKey = new System.Windows.Forms.Button();
			this.EditRegistryKey = new System.Windows.Forms.Button();
			this.RemoveRegistryKey = new System.Windows.Forms.Button();
			this.AddRegistryKey = new System.Windows.Forms.Button();
			this.button4 = new System.Windows.Forms.Button();
			this.ExecuteCompiler = new System.Windows.Forms.Button();
			this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
			this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog();
			this.mainMenu1 = new System.Windows.Forms.MainMenu();
			this.menuItem1 = new System.Windows.Forms.MenuItem();
			this.menuLoadDUSFile = new System.Windows.Forms.MenuItem();
			this.menuConvertQFE = new System.Windows.Forms.MenuItem();
			this.menuConvertInf = new System.Windows.Forms.MenuItem();
			this.menuItem5 = new System.Windows.Forms.MenuItem();
			this.menuGenerateScript = new System.Windows.Forms.MenuItem();
			this.menuExecuteCompiler = new System.Windows.Forms.MenuItem();
			this.menuItem2 = new System.Windows.Forms.MenuItem();
			this.menuClearAllFiles = new System.Windows.Forms.MenuItem();
			this.menuClearAllRegistry = new System.Windows.Forms.MenuItem();
			this.menuClearAll = new System.Windows.Forms.MenuItem();
			this.menuItem4 = new System.Windows.Forms.MenuItem();
			this.File_Exit = new System.Windows.Forms.MenuItem();
			this.menuItem3 = new System.Windows.Forms.MenuItem();
			this.Tools_Options = new System.Windows.Forms.MenuItem();
			this.Help_About = new System.Windows.Forms.MenuItem();
			this.btnParseRTF = new System.Windows.Forms.Button();
			this.rtxtQFEInfo = new System.Windows.Forms.RichTextBox();
			this.btnParseInf = new System.Windows.Forms.Button();
			this.groupBox1.SuspendLayout();
			this.groupBox2.SuspendLayout();
			this.SuspendLayout();
			// 
			// listView1
			// 
			this.listView1.AllowDrop = true;
			this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
																						this.Filename,
																						this.Action,
																						this.DestinationFolder});
			this.listView1.FullRowSelect = true;
			this.listView1.GridLines = true;
			this.listView1.HideSelection = false;
			this.listView1.Location = new System.Drawing.Point(16, 40);
			this.listView1.Name = "listView1";
			this.listView1.Size = new System.Drawing.Size(440, 112);
			this.listView1.TabIndex = 0;
			this.listView1.View = System.Windows.Forms.View.Details;
			this.listView1.DoubleClick += new System.EventHandler(this.listView1_DoubleClick);
			this.listView1.DragDrop += new System.Windows.Forms.DragEventHandler(this.listView1_DragDrop);
			this.listView1.DragEnter += new System.Windows.Forms.DragEventHandler(this.listView1_DragEnter);
			this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged);
			// 
			// Filename
			// 
			this.Filename.Text = "Source file";
			this.Filename.Width = 157;
			// 
			// Action
			// 
			this.Action.Text = "Action";
			this.Action.Width = 80;
			// 
			// DestinationFolder
			// 
			this.DestinationFolder.Text = "Destination";
			this.DestinationFolder.Width = 199;
			// 
			// groupBox1
			// 
			this.groupBox1.Controls.Add(this.EditItem);
			this.groupBox1.Controls.Add(this.button1);
			this.groupBox1.Controls.Add(this.AddFilesButton);
			this.groupBox1.Location = new System.Drawing.Point(8, 16);
			this.groupBox1.Name = "groupBox1";
			this.groupBox1.Size = new System.Drawing.Size(560, 144);
			this.groupBox1.TabIndex = 1;
			this.groupBox1.TabStop = false;
			this.groupBox1.Text = "Files";
			// 
			// EditItem
			// 
			this.EditItem.DialogResult = System.Windows.Forms.DialogResult.Cancel;
			this.EditItem.Enabled = false;
			this.EditItem.Location = new System.Drawing.Point(456, 88);
			this.EditItem.Name = "EditItem";
			this.EditItem.Size = new System.Drawing.Size(96, 23);
			this.EditItem.TabIndex = 2;
			this.EditItem.Text = "Edit File";
			this.EditItem.Click += new System.EventHandler(this.EditItem_Click);
			// 
			// button1
			// 
			this.button1.Enabled = false;
			this.button1.Location = new System.Drawing.Point(456, 56);
			this.button1.Name = "button1";
			this.button1.Size = new System.Drawing.Size(96, 23);
			this.button1.TabIndex = 1;
			this.button1.Text = "Remove File(s)";
			this.button1.Click += new System.EventHandler(this.button1_Click);
			// 
			// AddFilesButton
			// 
			this.AddFilesButton.Location = new System.Drawing.Point(456, 24);
			this.AddFilesButton.Name = "AddFilesButton";
			this.AddFilesButton.Size = new System.Drawing.Size(96, 23);
			this.AddFilesButton.TabIndex = 0;
			this.AddFilesButton.Text = "Add File(s)";
			this.AddFilesButton.Click += new System.EventHandler(this.AddFilesButton_Click);
			// 
			// groupBox2
			// 
			this.groupBox2.Controls.Add(this.listView2);
			this.groupBox2.Controls.Add(this.ImportRegKey);
			this.groupBox2.Controls.Add(this.EditRegistryKey);
			this.groupBox2.Controls.Add(this.RemoveRegistryKey);
			this.groupBox2.Controls.Add(this.AddRegistryKey);
			this.groupBox2.Location = new System.Drawing.Point(8, 168);
			this.groupBox2.Name = "groupBox2";
			this.groupBox2.Size = new System.Drawing.Size(560, 168);
			this.groupBox2.TabIndex = 2;
			this.groupBox2.TabStop = false;
			this.groupBox2.Text = "Registry";
			// 
			// listView2
			// 
			this.listView2.AllowDrop = true;
			this.listView2.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
																						this.TopLevelKey,
																						this.Key,
																						this.RegValue,
																						this.RegistryAction});
			this.listView2.FullRowSelect = true;
			this.listView2.GridLines = true;
			this.listView2.HideSelection = false;
			this.listView2.Location = new System.Drawing.Point(8, 24);
			this.listView2.Name = "listView2";
			this.listView2.Size = new System.Drawing.Size(440, 136);
			this.listView2.TabIndex = 1;
			this.listView2.View = System.Windows.Forms.View.Details;
			this.listView2.DoubleClick += new System.EventHandler(this.listView2_DoubleClick);
			this.listView2.DragDrop += new System.Windows.Forms.DragEventHandler(this.listView2_DragDrop);
			this.listView2.DragEnter += new System.Windows.Forms.DragEventHandler(this.listView2_DragEnter);
			this.listView2.SelectedIndexChanged += new System.EventHandler(this.listView2_SelectedIndexChanged);
			// 
			// TopLevelKey
			// 
			this.TopLevelKey.Text = "Node";
			this.TopLevelKey.Width = 120;
			// 
			// Key
			// 
			this.Key.Text = "Key";
			this.Key.Width = 115;
			// 
			// RegValue
			// 
			this.RegValue.Text = "Value";
			this.RegValue.Width = 104;
			// 
			// RegistryAction
			// 
			this.RegistryAction.Text = "Action";
			this.RegistryAction.Width = 97;
			// 
			// ImportRegKey
			// 
			this.ImportRegKey.DialogResult = System.Windows.Forms.DialogResult.Cancel;
			this.ImportRegKey.Location = new System.Drawing.Point(456, 24);
			this.ImportRegKey.Name = "ImportRegKey";
			this.ImportRegKey.Size = new System.Drawing.Size(96, 23);
			this.ImportRegKey.TabIndex = 2;
			this.ImportRegKey.Text = "Import Key(s)";
			this.ImportRegKey.Click += new System.EventHandler(this.ImportRegKey_Click);
			// 
			// EditRegistryKey
			// 
			this.EditRegistryKey.Enabled = false;
			this.EditRegistryKey.Location = new System.Drawing.Point(456, 120);
			this.EditRegistryKey.Name = "EditRegistryKey";
			this.EditRegistryKey.Size = new System.Drawing.Size(96, 23);
			this.EditRegistryKey.TabIndex = 5;
			this.EditRegistryKey.Text = "Edit Key";
			this.EditRegistryKey.Click += new System.EventHandler(this.EditRegistryItem_Click);
			// 
			// RemoveRegistryKey
			// 
			this.RemoveRegistryKey.Enabled = false;
			this.RemoveRegistryKey.Location = new System.Drawing.Point(456, 88);
			this.RemoveRegistryKey.Name = "RemoveRegistryKey";
			this.RemoveRegistryKey.Size = new System.Drawing.Size(96, 23);
			this.RemoveRegistryKey.TabIndex = 4;
			this.RemoveRegistryKey.Text = "Remove Key(s)";
			this.RemoveRegistryKey.Click += new System.EventHandler(this.RemoveRegKeys_Click);
			// 
			// AddRegistryKey
			// 
			this.AddRegistryKey.Location = new System.Drawing.Point(456, 56);
			this.AddRegistryKey.Name = "AddRegistryKey";
			this.AddRegistryKey.Size = new System.Drawing.Size(96, 23);
			this.AddRegistryKey.TabIndex = 3;
			this.AddRegistryKey.Text = "Add Key";
			this.AddRegistryKey.Click += new System.EventHandler(this.AddRegistryKey_Click);
			// 
			// button4
			// 
			this.button4.Location = new System.Drawing.Point(290, 344);
			this.button4.Name = "button4";
			this.button4.Size = new System.Drawing.Size(136, 23);
			this.button4.TabIndex = 6;
			this.button4.Text = "Generate Script";
			this.button4.Click += new System.EventHandler(this.button4_Click);
			// 
			// ExecuteCompiler
			// 
			this.ExecuteCompiler.Location = new System.Drawing.Point(431, 344);
			this.ExecuteCompiler.Name = "ExecuteCompiler";
			this.ExecuteCompiler.Size = new System.Drawing.Size(136, 23);
			this.ExecuteCompiler.TabIndex = 7;
			this.ExecuteCompiler.Text = "Execute Compiler";
			this.ExecuteCompiler.Click += new System.EventHandler(this.ExecuteCompiler_Click);
			// 
			// mainMenu1
			// 
			this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																					  this.menuItem1,
																					  this.menuItem3,
																					  this.Help_About});
			// 
			// menuItem1
			// 
			this.menuItem1.Index = 0;
			this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																					  this.menuLoadDUSFile,
																					  this.menuConvertQFE,
																					  this.menuConvertInf,
																					  this.menuItem5,
																					  this.menuGenerateScript,
																					  this.menuExecuteCompiler,
																					  this.menuItem2,
																					  this.menuClearAllFiles,
																					  this.menuClearAllRegistry,
																					  this.menuClearAll,
																					  this.menuItem4,
																					  this.File_Exit});
			this.menuItem1.Text = "File";
			// 
			// menuLoadDUSFile
			// 
			this.menuLoadDUSFile.Index = 0;
			this.menuLoadDUSFile.Text = "Load DUS file...";
			this.menuLoadDUSFile.Click += new System.EventHandler(this.menuLoadDUSFile_Click);
			// 
			// menuConvertQFE
			// 
			this.menuConvertQFE.Index = 1;
			this.menuConvertQFE.Text = "Convert QFE to DUA...";
			this.menuConvertQFE.Click += new System.EventHandler(this.menuConvertQFE_Click);
			// 
			// menuConvertInf
			// 
			this.menuConvertInf.Index = 2;
			this.menuConvertInf.Text = "Convert INF to DUA...";
			this.menuConvertInf.Click += new System.EventHandler(this.menuConvertInf_Click);
			// 
			// menuItem5
			// 
			this.menuItem5.Index = 3;
			this.menuItem5.Text = "-";
			// 
			// menuGenerateScript
			// 
			this.menuGenerateScript.Index = 4;
			this.menuGenerateScript.Text = "Generate script...";
			this.menuGenerateScript.Click += new System.EventHandler(this.menuGenerateScript_Click);
			// 
			// menuExecuteCompiler
			// 
			this.menuExecuteCompiler.Index = 5;
			this.menuExecuteCompiler.Text = "Execute compiler";
			this.menuExecuteCompiler.Click += new System.EventHandler(this.menuExecuteCompiler_Click);
			// 
			// menuItem2
			// 
			this.menuItem2.Index = 6;
			this.menuItem2.Text = "-";
			// 
			// menuClearAllFiles
			// 
			this.menuClearAllFiles.Index = 7;
			this.menuClearAllFiles.Text = "Clear all files";
			this.menuClearAllFiles.Click += new System.EventHandler(this.menuClearAllFiles_Click);
			// 
			// menuClearAllRegistry
			// 
			this.menuClearAllRegistry.Index = 8;
			this.menuClearAllRegistry.Text = "Clear all registry";
			this.menuClearAllRegistry.Click += new System.EventHandler(this.menuClearAllRegistry_Click);
			// 
			// menuClearAll
			// 
			this.menuClearAll.Index = 9;
			this.menuClearAll.Text = "Clear all files and registry";
			this.menuClearAll.Click += new System.EventHandler(this.menuClearAll_Click);
			// 
			// menuItem4
			// 
			this.menuItem4.Index = 10;
			this.menuItem4.Text = "-";
			// 
			// File_Exit
			// 
			this.File_Exit.Index = 11;
			this.File_Exit.Text = "Exit";
			this.File_Exit.Click += new System.EventHandler(this.File_Exit_Click);
			// 
			// menuItem3
			// 
			this.menuItem3.Index = 1;
			this.menuItem3.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																					  this.Tools_Options});
			this.menuItem3.Text = "Tool";
			// 
			// Tools_Options
			// 
			this.Tools_Options.Index = 0;
			this.Tools_Options.Text = "Options...";
			this.Tools_Options.Click += new System.EventHandler(this.Tools_Options_Click);
			// 
			// Help_About
			// 
			this.Help_About.Index = 2;
			this.Help_About.Text = "About";
			this.Help_About.Click += new System.EventHandler(this.menuItem2_Click);
			// 
			// btnParseRTF
			// 
			this.btnParseRTF.Location = new System.Drawing.Point(8, 344);
			this.btnParseRTF.Name = "btnParseRTF";
			this.btnParseRTF.Size = new System.Drawing.Size(136, 23);
			this.btnParseRTF.TabIndex = 3;
			this.btnParseRTF.Text = "Convert QFE to DUA";
			this.btnParseRTF.Click += new System.EventHandler(this.btnParseRTF_Click);
			// 
			// rtxtQFEInfo
			// 
			this.rtxtQFEInfo.Location = new System.Drawing.Point(8, 0);
			this.rtxtQFEInfo.Name = "rtxtQFEInfo";
			this.rtxtQFEInfo.Size = new System.Drawing.Size(16, 8);
			this.rtxtQFEInfo.TabIndex = 4;
			this.rtxtQFEInfo.TabStop = false;
			this.rtxtQFEInfo.Text = "";
			this.rtxtQFEInfo.Visible = false;
			// 
			// btnParseInf
			// 
			this.btnParseInf.Location = new System.Drawing.Point(149, 344);
			this.btnParseInf.Name = "btnParseInf";
			this.btnParseInf.Size = new System.Drawing.Size(136, 23);
			this.btnParseInf.TabIndex = 5;
			this.btnParseInf.Text = "Convert INF to DUA";
			this.btnParseInf.Click += new System.EventHandler(this.btnParseInf_Click);
			// 
			// Form1
			// 
			this.AllowDrop = true;
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(576, 376);
			this.Controls.Add(this.btnParseInf);
			this.Controls.Add(this.rtxtQFEInfo);
			this.Controls.Add(this.btnParseRTF);
			this.Controls.Add(this.ExecuteCompiler);
			this.Controls.Add(this.button4);
			this.Controls.Add(this.groupBox2);
			this.Controls.Add(this.listView1);
			this.Controls.Add(this.groupBox1);
			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
			this.MaximizeBox = false;
			this.Menu = this.mainMenu1;
			this.Name = "Form1";
			this.Text = "DUA Script Generator v1.1";
			this.Load += new System.EventHandler(this.Form1_Load);
			this.groupBox1.ResumeLayout(false);
			this.groupBox2.ResumeLayout(false);
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] strArguments) 
		{
			Form1 myForm = new Form1();

			if ( (null == strArguments) || (0 == strArguments.Length) )
			{
				// If no command line parameters are passed in, show the UI
				Application.Run(myForm);
			}
			else
			{
				// If there are any command line parameters, try to parse them
				myForm.ParseCommandLineParameters(strArguments);
			}
		}

		private void ParseCommandLineParameters(string [] strArguments)
		{
			string strCurrentArg;
			string strInputFilename = "";
			string strOutputFilename = "";

			if ( (null == strArguments) || (0 == strArguments.Length) )
			{
				// If a bad input parameter was passed in, show the usage
				// dialog and bail out
				ShowUsageDialog();
				return;
			}

			for (int i = 0; i < strArguments.Length; i++)
			{
				strCurrentArg = strArguments[i];

				// Check to see if the current argument start with a dash or forward slash
				if ( (strCurrentArg.StartsWith("-")) || (strCurrentArg.StartsWith("/")) )
				{
					// Skip the dash or forward slash
					strCurrentArg = strCurrentArg.Substring(1).ToLower();

					switch (strCurrentArg)
					{
						// Input filename
						case "i":
							// Try to get the next parameter, which should be the input filename
							i++;
							if (i >= strArguments.Length)
							{
								ShowUsageDialog();
								return;
							}

							strInputFilename = strArguments[i];
							break;

						// Output filename
						case "o":
							// Try to get the next parameter, which should be the output filename
							i++;
							if (i >= strArguments.Length)
							{
								ShowUsageDialog();
								return;
							}

							strOutputFilename = strArguments[i];
							break;

						// Usage information
						case "?":
							ShowUsageDialog();
							return;

						// Ignore unrecognized switches
						default:
							break;
					}
				}
			}

			// Input and output filenames are required, so make sure they exist and if not, bail out
			if ( ("" == strInputFilename) || ("" == strOutputFilename) )
			{
				ShowUsageDialog();
				return;
			}

			// Make sure the input filename exists
			if (false == System.IO.File.Exists(strInputFilename))
			{
				MessageBox.Show("The specified input file '" + strInputFilename + "' does not exist", "DUAScriptGen Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			// Check the file extension of the input file and call the appropriate parsing method
			strInputFilename.ToLower();
			if ( (strInputFilename.EndsWith(".htm")) || (strInputFilename.EndsWith(".html")) )
			{
				// Parse the HTML file
				ParseQFEHtmlFile(strInputFilename);
			}
			else if (strInputFilename.EndsWith(".inf"))
			{
				// Build the path to Inf2Rtf which is needed for parsing QFE INF files
				BuildInf2RtfPath();

				// Parse the INF file
				ParseQFEInfFile(strInputFilename);
			}
			else
			{
				// If the file is not named *.htm or *.inf, then treat it as an RTF
				// file and try to parse it accordingly
				ParseQFEFile(strInputFilename);
			}

			// Generate the DUS file here
			GenerateDUAScript(strOutputFilename, true);
		}

		private void ShowUsageDialog()
		{
			MessageBox.Show(g_strUsageInfo, "DUAScriptGen Usage", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}

		private void listView1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
		{
			if (e.Data.GetDataPresent(DataFormats.FileDrop))
			{
				string[] filenames =(string[])e.Data.GetData(DataFormats.FileDrop);

				foreach ( string filename in filenames )
				{
					// Pull the file information and add to the list view
					ParseFileEntry(filename, "", "", true, (int)E_FILEOPERATION.Move, false);
				}
			}		
		}

		private void listView1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
		{
			if (e.Data.GetDataPresent(DataFormats.FileDrop)) 
			{
				e.Effect=DragDropEffects.Copy;
			}
			else 
			{
				e.Effect=DragDropEffects.None;
			}		
		}

		private void AddRegistryKey_Click(object sender, System.EventArgs e)
		{
			// Create the registry value edit dialog and show it
			Registry_EditItem eItem = new Registry_EditItem();
			eItem.Text = "Add Registry Data Item";
			eItem.ShowDialog();

			// Update the data and UI if the user pressed OK on the dialog
			if (DialogResult.OK == eItem.DialogResult) 
			{
				AddRegistryEntryToListView(g_strRegRoots[eItem.m_dRegEntry.m_iRegRoot],
					eItem.m_dRegEntry.m_strRegKeyName,
					eItem.m_dRegEntry.m_strRegValueName,
					eItem.m_dRegEntry.m_strRegValueData,
					eItem.m_dRegEntry.m_iRegValueDataType,
					eItem.m_dRegEntry.m_iRegistryOperation);
			}
		}


		private void ImportRegKey_Click(object sender, System.EventArgs e)
		{
			openFileDialog1.FileName = "";
			openFileDialog1.DefaultExt = "reg";
			openFileDialog1.Filter = "Registry files (*.reg)|*.reg|All files (*.*)|*.*";
			
			DialogResult result = openFileDialog1.ShowDialog();
			
			// Parse the registry file if the OK button was pressed
			if (result == DialogResult.OK) 
			{
				ParseRegistryFile(openFileDialog1.FileName);
			}
		}


		/*****************************************************************************************
		Function Name:	listView2_DragDrop
		Author:			aaronste
		Description:	Parses files that are dragged and dropped onto listView2
		Inputs:			object sender - calling object
						DragEventArgs e - container for drag-n-drop parameters
		Outputs:		none
		******************************************************************************************/
		private void listView2_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
		{
			if (e.Data.GetDataPresent(DataFormats.FileDrop))
			{
				// Retrieve all of the names of the files dropped onto the list view
				string[] strFilenames =(string[])e.Data.GetData(DataFormats.FileDrop);

				// Try to parse each of the registry files
				foreach (string strFilename in strFilenames)
				{
					ParseRegistryFile(strFilename);
				}
			}		
		}

		/*****************************************************************************************
		Function Name:	listView2_DragEnter
		Author:			aaronste
		Description:	Updates animation when objects are dragged and dropped onto listView2
		Inputs:			object sender - calling object
						DragEventArgs e - container for drag-n-drop parameters
		Outputs:		none
		******************************************************************************************/
		private void listView2_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
		{
			if (e.Data.GetDataPresent(DataFormats.FileDrop)) 
			{
				e.Effect=DragDropEffects.Copy;
			}
			else 
			{
				e.Effect=DragDropEffects.None;
			}		
		}


		/*****************************************************************************************
		Function Name:	ParseFileEntry
		Author:			mikehall
		Description:	Takes a filename and creates an entry for it in the DUA file list view
		Inputs:			string strFilename	- full path of the file to add
		Outputs:		true if successful
						false if not
		******************************************************************************************/
		private bool ParseFileEntry(string strSourceFilename, string strDestFilename, string strExecuteCmdLine, bool bCreateDirectory, int iFileOperation, bool bRebootRequired)
		{
			// Check the input parameters
			if ("" == strSourceFilename)
				return false;

			string strDestFolder = "", strShortDestFilename = "";

			// Retrieve the short source filename in case a fully qualified path
			// to the file was passed in
			string strShortSourceFilename="";
			int iIndex = strSourceFilename.LastIndexOf("\\");
			if (-1 != iIndex) 
				strShortSourceFilename = strSourceFilename.Substring(iIndex+1);
			else 
				strShortSourceFilename = strSourceFilename;

			// Retrieve the short destination filename in case a fully qualified path
			// to the file was passed in
			if ("" == strDestFilename)
			{
				strDestFolder = "";
				strShortDestFilename = strShortSourceFilename;
			}
			else
			{
				iIndex = strDestFilename.LastIndexOf("\\");
				if (-1 != iIndex)
				{
					strDestFolder = strDestFilename.Substring(0, iIndex);
					strShortDestFilename = strDestFilename.Substring(iIndex+1);
				}
				else
				{
					strDestFolder = "";
					strShortDestFilename = strShortSourceFilename;
				}
			}

			// Remove drive letter from the destination folder if it was included
			iIndex = strDestFolder.IndexOf("\\");
			if (-1 != iIndex)
			{
				strDestFolder = strDestFolder.Substring(iIndex);
			}
			
			// Add the file to the list view
			AddFileEntryToListView(strShortSourceFilename,
				strDestFolder,
				strShortDestFilename,
				strExecuteCmdLine,
				bCreateDirectory,
				iFileOperation,
				bRebootRequired);
			
			return true;
		}


		/*****************************************************************************************
		Function Name:	ParseDusFile
		Author:			aaronste
		Description:	Loops through a DUS file and adds the file and registry information
						to the list views in the UI
		Inputs:			string strFilename	- full path of the file to parse
		Outputs:		true if successful
						false if not
		Notes:			This function is designed to reverse engineer a DUS file that was
						previously created by this tool.  It may or may not work with DUS
						files created by hand, and will not try to parse DUS files that
						do not have some of the tokens that are written by this tool
		******************************************************************************************/
		private bool ParseDusFile(string strFilename)
		{
			// Check input parameter
			if (null == strFilename)
				return false;

			// Make sure the input file exists
			if (false == System.IO.File.Exists(strFilename))
				return false;

			string strLine = "";
			bool bRetValue = true;

			// Create a stream reader to parse the file
			StreamReader sr = new StreamReader(strFilename);

			// Check the first line to see if it contains the DUS file beginning marker
			if ((strLine = sr.ReadLine()) == null)
			{
				bRetValue = false;
				goto CLEANUP;
			}

			// We need to search for the substring with the short version of the 
			// header name to enable backwards compatibility (meaning that v1.1 can
			// read files created by v1.0).  Forward compat will only work beginning
			// with DUAScriptGen v1.1.0001 because this was previously doing a full
			// string match on the first line of the DUS file, which includes a
			// version number in it...
			if (-1 == strLine.IndexOf(g_strDuaScriptGenHeaderShort))
			{
				MessageBox.Show("The file " + strFilename + " is not recognized as being created by DUAScriptGen so it will not be loaded.", "Error - unrecognized DUS file format", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				bRetValue = false;
				goto CLEANUP;
			}

			// If we get here, we found the DUS file beginning marker, so continue parsing the file
			while ((strLine = sr.ReadLine()) != null) 
			{
				// Search for the beginning of the file section
				if (strLine == g_strFileSectionBegin)
				{
					ReadDusFileEntries(sr);
				}

				// Search for the beginning of the registry section
				if (strLine == g_strRegistrySectionBegin)
				{
					ReadDusRegistryEntries(sr);
				}

				// Search for the beginning of the post-reboot execute section
				if (strLine == g_strPostRebootExecuteSectionBegin)
				{
					ReadDusPostRebootExecuteEntries(sr);
				}
			}

			// Clean up
			CLEANUP:
				sr.Close();

			return bRetValue;
		}


		private bool ReadDusFileEntries(StreamReader sr)
		{
			string strLine;
			string strNextLine;
			string [] strFileTokens;
			int iFileOperation;

			string strSourceFile = "";
			string strDestFile = "";
			string strExecuteCmd = "";
			bool bFoundCreateDirectory = false;
			bool bFoundMoveFile = false;
			bool bFoundCopyFile = false;
			bool bFoundExecuteCmd = false;
			bool bRebootRequired = false;

			// Loop through the file until we reach the end of the file section or the 
			// end of the file
			while ( ((strLine = sr.ReadLine()) != null) && (strLine != g_strFileSectionEnd) )
			{
				// Skip blank lines and comments
				if ( ("" == strLine) || (strLine.StartsWith("//")) )
					continue;

				// Check to see if we found a delete file command
				if (strLine.StartsWith("DELETEFILE"))
				{
					// Tokenize the DeleteFile string using comma delimiters
					strFileTokens = strLine.Split(',');

					// Add the entry to the list view with the values read from the DUS file
					// and set it to the delete file action type
					// Valid deletefile entries should have 4 comma-delimited entries
					if (strFileTokens.Length == 4)
					{
						// Add the delete file entry to the list view
						ParseFileEntry(strFileTokens[3], strFileTokens[3], "", true, (int)E_FILEOPERATION.Delete, false);
					}

					// Reset tracking variables
					strSourceFile = "";
					strDestFile = "";
					strExecuteCmd = "";
					bFoundCreateDirectory = false;
					bFoundMoveFile = false;
					bFoundCopyFile = false;
					bFoundExecuteCmd = false;
					bRebootRequired = false;

					continue;
				}

				// Check to see if we found a create directory command
				else if (strLine.StartsWith("CREATEDIRECTORY"))
				{
					// Set the boolean flag for later use
					bFoundCreateDirectory = true;
					continue;
				}

				// Check to see if we found a MoveFile or CopyFile command
				else if ( (strLine.StartsWith("MOVEFILE")) || (strLine.StartsWith("COPYFILE")) )
				{
					// Tokenize the string using comma delimiters
					strFileTokens = strLine.Split(',');

					// Add the entry to the list view with the values read from the DUS file
					// and set it to the create directory and move file action type
					// Valid copyfile/movefile entries should have 6 comma-delimited entries
					if (strFileTokens.Length == 7)
					{
						// Check to see if this action is a copy file or move file action
						if (strLine.StartsWith("MOVEFILE"))
							bFoundMoveFile = true;

						if (strLine.StartsWith("COPYFILE"))
							bFoundCopyFile = true;

						// Save the source and destination file names for later use
						strSourceFile = strFileTokens[3];
						strDestFile = strFileTokens[5];

						// Check to see if the current command is a MOVEFILE associated with a file in use
						if ( (bFoundMoveFile) && (-1 != strLine.IndexOf(g_strFileInUseRenameExtension)) )
						{
							// If we found a MOVEFILE associated with a file in use, we need to check
							// the next line and see if it is also a MOVEFILE.  If so, this is a copy
							// command with reboot required.  If not, this is a delete command with
							// reboot required
							if ( ((strNextLine = sr.ReadLine()) != null) && (strNextLine.StartsWith("MOVEFILE")) )
							{
								// Tokenize the string using comma delimiters
								strFileTokens = strNextLine.Split(',');

								// Need to use the 2nd MOVEFILE command to determine the correct source and
								// destination filenames
								if (strFileTokens.Length == 7)
								{
									// Save the source and destination file names for later use
									strSourceFile = strFileTokens[3];
									strDestFile = strFileTokens[5];

									// Update the tracking variables for later use - this command is
									// actually a copy file with reboot required as opposed to a move file
									bFoundMoveFile = false;
									bFoundCopyFile = true;
									bRebootRequired = true;
								}
							}
							else
							{
								// If we get here, the operation is a delete with reboot required
								// Add the delete file entry to the list view
								ParseFileEntry(strSourceFile, strSourceFile, "", true, (int)E_FILEOPERATION.Delete, true);

								// Reset tracking variables
								strSourceFile = "";
								strDestFile = "";
								strExecuteCmd = "";
								bFoundCreateDirectory = false;
								bFoundMoveFile = false;
								bFoundCopyFile = false;
								bFoundExecuteCmd = false;
								bRebootRequired = false;

								continue;
							}
						}

						// Check to see if the next line is an execute command
						if ( ((strNextLine = sr.ReadLine()) != null) && (strNextLine.StartsWith("EXECUTEPROCESS")) )
						{
							// Tokenize the execute command using comma delimiters
							strFileTokens = strNextLine.Split(',');

							// Valid executeprocess entries should have 24 comma-delimited entries
							if (strFileTokens.Length == 24)
							{
								// Set the flag and save the execute command for later use
								bFoundExecuteCmd = true;
								strExecuteCmd = strFileTokens[6];
							}
						}
					}
					else
					{
						// Reset the createdirectory boolean flag if we got an invalid
						// copyfile/movefile entry
						bFoundCreateDirectory = false;
						continue;
					}
				}
				
				// Check to see if we found an execute command
				else if (strLine.StartsWith("EXECUTEPROCESS"))
				{
					// Tokenize the string using comma delimiters
					strFileTokens = strLine.Split(',');

					// Valid executeprocess entries should have 24 comma-delimited entries
					if (strFileTokens.Length == 24)
					{
						bFoundExecuteCmd = true;
						strSourceFile = strFileTokens[4];
						strDestFile = strFileTokens[4];
						strExecuteCmd = strFileTokens[6];
					}
				}

				// Check and see if we need to add an entry to the list view
				if ( (true == bFoundExecuteCmd) || (true == bFoundMoveFile) || (true == bFoundCopyFile) )
				{
					iFileOperation = -1;

					// Determine which file operation should be used for this entry
					if ( (true == bFoundExecuteCmd) && (false == bFoundMoveFile) && (false == bFoundCopyFile) )
						iFileOperation = (int)E_FILEOPERATION.Execute;

					if (true == bFoundMoveFile)
					{
						if (false == bFoundExecuteCmd)
							iFileOperation = (int)E_FILEOPERATION.Move;
						else
							iFileOperation = (int)E_FILEOPERATION.MoveAndExecute;
					}

					if (true == bFoundCopyFile)
					{
						if (false == bFoundExecuteCmd)
							iFileOperation = (int)E_FILEOPERATION.Copy;
						else
							iFileOperation = (int)E_FILEOPERATION.CopyAndExecute;
					}

					// Add the file entry to the list view as long as we do not
					// have an invalid file operation value
					if (-1 != iFileOperation)
						ParseFileEntry(strSourceFile, strDestFile, strExecuteCmd, bFoundCreateDirectory, iFileOperation, bRebootRequired);

					// Reset tracking variables
					strSourceFile = "";
					strDestFile = "";
					strExecuteCmd = "";
					bFoundCreateDirectory = false;
					bFoundMoveFile = false;
					bFoundCopyFile = false;
					bFoundExecuteCmd = false;
					bRebootRequired = false;
				}
			}

			return true;
		}


		private bool ReadDusRegistryEntries(StreamReader sr)
		{
			string strLine;
			string strNextLine;
			string strRegValueData;
			string [] strTokens;
			int iRegValueDataType;
			int i;

			// Loop through the file until we reach the end of the registry section or the 
			// end of the file
			while ( ((strLine = sr.ReadLine()) != null) && (strLine != g_strRegistrySectionEnd) )
			{
				strRegValueData = "";
				iRegValueDataType = 0;
				i = 8;

				// Check to see if we found a create key command
				if (strLine.StartsWith("REGCREATEKEY"))
				{
					// Check to see if the next line is a set value command
					if ( ((strNextLine = sr.ReadLine()) != null) && (strNextLine.StartsWith("REGSETVALUE")) )
					{
						// Tokenize the regsetvalue string using comma delimiters
						strTokens = strNextLine.Split(',');

						// Add the entry to the list view with the values read from the file
						// and set it to the create and add value action type
						if (strTokens.Length > 7)
						{
							// Make sure the token for registry value data type is at least 3 
							// characters long since it should be of the format DA<type_name>
							// (DAREG_SZ for example)
							if (strTokens[7].Length < 3)
								continue;

							// Due to XPe bug 9641, dword values have to be written and read
							// as REG_DWORD_LITTLE_ENDIAN, so we need to convert the string
							// to the REG_DWORD value that this tool understands
							if (-1 != strTokens[7].IndexOf("REG_DWORD_LITTLE_ENDIAN"))
								iRegValueDataType = (int)E_REGVALUEDATATYPE.REG_DWORD;
							else
								iRegValueDataType = Array.IndexOf(g_strRegValueDataTypes, strTokens[7].Substring(2));

							// The registry value size is only needed for certain
							// data types, so check for that case and update the
							// starting point for parsing the registry data value
							if (true == IsRegistryDataSizeParameterRequired(iRegValueDataType))
								i = 9;

							// The registry value data could be a comma-delimited list, so
							// reassemble it here if so
							for (; i < strTokens.Length; i++)
							{
								// Add a comma for all tokens except the first one
								if ("" != strRegValueData)
									strRegValueData += ",";

								strRegValueData += strTokens[i];
							}

							// Remove white space from the reg value data
							strRegValueData = strRegValueData.Trim();

							AddRegistryEntryToListView(strTokens[2], strTokens[4], strTokens[6], strRegValueData, iRegValueDataType, (int)E_REGISTRYOPERATION.CreateAndAddValue);
						}
					}
					else
					{
						// Tokenize the regcreatekey string using comma delimiters
						strTokens = strLine.Split(',');

						// Add the entry to the list view with the values read from the file
						// and set it to the create key action type
						if (strTokens.Length > 4)
						{
							AddRegistryEntryToListView(strTokens[2], strTokens[4], "", "", 0, (int)E_REGISTRYOPERATION.Create);
						}
					}
				}

				// Check to see if we found a delete key command
				if (strLine.StartsWith("REGDELETEKEY"))
				{
					// Tokenize the regdeletekey string using comma delimiters
					strTokens = strLine.Split(',');

					// Add the entry to the list view with the values read from the file
					// and set it to the delete key action type
					if (strTokens.Length > 4)
					{
						AddRegistryEntryToListView(strTokens[2], strTokens[4], "", "", 0, (int)E_REGISTRYOPERATION.DeleteKey);
					}
				}

				// Check to see if we found a delete value command
				if (strLine.StartsWith("REGDELETEVALUE"))
				{
					// Tokenize the regdeletevalue string using comma delimiters
					strTokens = strLine.Split(',');

					// Add the entry to the list view with the values read from the file
					// and set it to the delete value action type
					if (strTokens.Length > 6)
					{
						AddRegistryEntryToListView(strTokens[2], strTokens[4], strTokens[6], "", 0, (int)E_REGISTRYOPERATION.DeleteValue);
					}
				}

				// TODO - implement these later
				if (strLine.StartsWith("REGSAVEKEY"))
				{
				}
				if (strLine.StartsWith("REGRESTOREKEY"))
				{
				}
			}

			return true;
		}


		private bool ReadDusPostRebootExecuteEntries(StreamReader sr)
		{
			DUAFile dFile;
			string strLine, strDestLocation;
			string [] strFileTokens;
			int x = 0;
			int iCount = listView1.Items.Count;

			// Loop through the file until we reach the end of the post-reboot execute
			// section or the end of the file
			while ( ((strLine = sr.ReadLine()) != null) && (strLine != g_strPostRebootExecuteSectionEnd) )
			{
				// Skip blank lines and comments
				if ( ("" == strLine) || (strLine.StartsWith("//")) )
					continue;
				
				// Check to see if we found an execute command
				if (strLine.StartsWith("EXECUTEPROCESS"))
				{
					// Tokenize the string using comma delimiters
					strFileTokens = strLine.Split(',');

					// Valid executeprocess entries should have 24 comma-delimited entries
					if (strFileTokens.Length == 24)
					{
						// A post-reboot execute command can only be written by a copy-and-execute
						// file operation that requires a reboot.  However, when reading the file
						// in the file section, it will appear to be a copy command with a reboot
						// required.  If we found an EXECUTEPROCESS entry in the DUS file, we
						// need to find the matching copy-and-execute file operation and update
						// the operation flag for it

						// Loop through all file entries and search for a copy-and-execute entry
						// that has the same source file name as the one we just found
						iCount = listView1.Items.Count;
						for(x = 0; x < iCount; x++) 
						{
							dFile = (DUAFile)listView1.Items[x].Tag;

							// If the current file has a copy command, if the reboot flag is set to true 
							// and if the destination file name equals the one we just read from the file,
							// then set the operation to copy-and-execute and set the execute command line
							// Note - token index 4 in strFileTokens is the destination file name
							if ( ((int)E_FILEOPERATION.Copy == dFile.Operation) && (true == dFile.bRebootRequired) )
							{
								strDestLocation = dFile.DestLocation + "\\" + dFile.DestFile;
								if (strFileTokens[4] == strDestLocation)
								{
									dFile.Operation = (int)E_FILEOPERATION.CopyAndExecute;
									dFile.ExecuteCommandLine = strFileTokens[6];

									// Update the text displayed in the list view to reflect the change
									// we just made
									listView1.Items[x].SubItems.RemoveAt(1);
									listView1.Items[x].SubItems.RemoveAt(1);
									listView1.Items[x].SubItems.Add(g_strFileOperations[(int)E_FILEOPERATION.CopyAndExecute]);
									listView1.Items[x].SubItems.Add(strDestLocation);

									// Break out of the loop because we will only have one file associated
									// with any given execute command
									break;
								}
							}
						}
					}
				}
			}

			return true;
		}


		/*****************************************************************************************
		Function Name:	ParseQFEFile
		Author:			aaronste
		Description:	Loops through a file and attempts to extract file
						and registry information from it.  If it is not the
						format of an XPe QFE release notes doc then it will
						exit without adding any files/keys to the list views
						in the UI
		Inputs:			string strFilename	- full path of the file to parse
		Outputs:		true if successful
						false if not
		******************************************************************************************/
		private bool ParseQFEFile(string strFilename)
		{
			// Check input parameter
			if (null == strFilename)
				return false;

			// Make sure the input file exists
			if (false == System.IO.File.Exists(strFilename))
				return false;

			LOADFILE:
				try
				{
					// Load the file into the hidden rich text control
					rtxtQFEInfo.LoadFile(strFilename, RichTextBoxStreamType.RichText);
				}
				catch(Exception e)
				{
					// Give the user the chance to retry
					if (DialogResult.Retry == MessageBox.Show(e.Message, "Error opening file", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error))
						goto LOADFILE;

					return false;
				}

			string [] strTextLines;
			string strTextToParse = rtxtQFEInfo.Text;

			// Tokenize the input string using carriage return delimiters
			strTextLines = strTextToParse.Split('\n');
			
			// Loop through the array of lines of text and process each file
			// and registry key listed within them
			for (int i = 0; i < strTextLines.Length; i++)
			{
				// Note - delimiters for file and registry sections are the end of the list and a blank line

				// Search for text indicating that we have found the file list
				if (-1 != strTextLines[i].IndexOf("changed files"))
				{
					// Skip the current line
					i++;

					// Loop through all the files in this list
					// Note - we need to skip the table header first
					while (-1 != strTextLines[i].IndexOf("Repository Source"))
						i++;

					// Some QFEs also have a template filename at
					// the top of the table - skip it also
					while (-1 != strTextLines[i].IndexOf("_FILENAME.EXT"))
						i++;

					// Now loop through the files listed in the table
					while ( (i < strTextLines.Length) && ("" != strTextLines[i].Trim()) && (-1 == strTextLines[i].IndexOf("registry addition")) && (-1 == strTextLines[i].IndexOf("registry deletion")) )
					{
						ParseFileRTF(strTextLines[i].Trim());
						i++;
					}
				}
				
				// Search for text indicating that we have found the registry add list
				if (-1 != strTextLines[i].IndexOf("registry addition"))
				{
					// Skip the current line
					i++;

					// Loop through all the registry keys in this list
					// Note - we need to skip the table header first
					while (-1 != strTextLines[i].IndexOf("Key Name\t"))
						i++;

					// Now loop through the registry keys listed in the table
					while ( (i < strTextLines.Length) && ("" != strTextLines[i].Trim()) && (-1 == strTextLines[i].IndexOf("registry deletion")) )
					{
						ParseRegistryRTF(strTextLines[i].Trim(), E_REGPARSEOPTION.Add);
						i++;
					}
				}

				// Search for text indicating that we have found the registry delete list
				if (-1 != strTextLines[i].IndexOf("registry deletion"))
				{
					// Skip the current line
					i++;

					// Loop through all the registry keys in this list
					// Note - we need to skip the table header first
					while (-1 != strTextLines[i].IndexOf("Key Name\t"))
						i++;

					// Now loop through the registry keys listed in the table
					while ( (i < strTextLines.Length) && ("" != strTextLines[i].Trim()) )
					{
						ParseRegistryRTF(strTextLines[i].Trim(), E_REGPARSEOPTION.Remove);
						i++;
					}
				}
			}

			return true;
		}
	

		/*****************************************************************************************
		Function Name:	ParseQFEInfFile
		Author:			aaronste
		Description:	Uses inf2rtf to convert an INF file into an HTML-formatted
						additional info file and then parses that file into a DUS file
		Inputs:			string strFilename	- full path of the INF file to parse
		Outputs:		true if successful
						false if not
		******************************************************************************************/
		private bool ParseQFEInfFile(string strFilename)
		{
			// Check input parameter
			if (null == strFilename)
				return false;

			// Make sure the input file exists
			if (false == System.IO.File.Exists(strFilename))
				return false;

			// Make sure inf2rtf exists
			if (false == System.IO.File.Exists(g_strInf2RtfPath))
				return false;

			// Build output file names to use with inf2rtf
			string strGeneratedHtmlFile = System.IO.Path.GetTempFileName();
			string strInf2RtfErrorFile = System.IO.Path.GetTempFileName();

			// Call inf2rtf to generate the additional info HTML file
			Process processInf2Rtf = new Process();
			processInf2Rtf.StartInfo.UseShellExecute = false;
			processInf2Rtf.StartInfo.FileName = g_strInf2RtfPath;
			processInf2Rtf.StartInfo.Arguments = "/i " + strFilename + " /o " + strGeneratedHtmlFile + " /e " + strInf2RtfErrorFile + " /q";
			processInf2Rtf.Start();

			// Wait for the inf2rtf process to finish
			processInf2Rtf.WaitForExit();

			// Pass the generated HTML file to the parsing method to create a DUS file
			ParseQFEHtmlFile(strGeneratedHtmlFile);

			// Try to delete the temporary files
			try
			{
				System.IO.File.Delete(strGeneratedHtmlFile);
				System.IO.File.Delete(strInf2RtfErrorFile);
			}
			catch
			{
				// Do nothing if delete fails - they are just temp files
			}

			return true;
		}


		/*****************************************************************************************
		Function Name:	ParseQFEHtmlFile
		Author:			aaronste
		Description:	Loops through an HTML QFE and attempts to extract file
						and registry information from it.  If it is not the
						format of an XPe HTML QFE release notes doc then it will
						exit without adding any files/keys to the list views
						in the UI
		Inputs:			string strFilename	- full path of the file to parse
		Outputs:		true if successful
						false if not
		******************************************************************************************/
		private bool ParseQFEHtmlFile(string strFilename)
		{
			StreamReader sr;
			string strLine;

			// Check input parameter
			if (null == strFilename)
				return false;

			// Make sure the input file exists
			if (false == System.IO.File.Exists(strFilename))
				return false;

			// Create a stream reader to parse the file
			bool bInTableRow = false;
			string strTableRow = "";
			int iIndex;
			int iCurrentSection = (int)E_RELNOTE_SECTION.None;

			sr = new StreamReader(strFilename);
			while ((strLine = sr.ReadLine()) != null) 
			{
				// Search for text indicating that we have found a list of files or reg keys
				if (-1 != strLine.IndexOf("changed files"))
					iCurrentSection = (int)E_RELNOTE_SECTION.FileAdd;
				else if (-1 != strLine.IndexOf("registry additions"))
					iCurrentSection = (int)E_RELNOTE_SECTION.RegistryAdd;
				else if (-1 != strLine.IndexOf("registry deletions"))
					iCurrentSection = (int)E_RELNOTE_SECTION.RegistryRemove;

				if ((int)E_RELNOTE_SECTION.None != iCurrentSection)
				{
					// Now we need to parse the HTML table and find each row with
					// a file or registry entry
					while ( ((strLine = sr.ReadLine()) != null) && (-1 == strLine.IndexOf("</table>")) )
					{
						if (!bInTableRow)
						{
							// Search for a table row opening item
							iIndex = strLine.IndexOf("<tr");
							if (-1 != iIndex)
							{
								// Start saving the table row to a separate string
								strTableRow = strLine.Substring(iIndex);
								bInTableRow = true;
							}
						}
						else
						{
							// Search for a table row closing item
							iIndex = strLine.IndexOf("</tr>");
							if (-1 != iIndex)
							{
								// Close off the table row string and parse it
								// We need to add 5 to the index to account for the 
								// length of "</tr>"
								strTableRow += strLine.Substring(0, iIndex + 5);

								// Parse the elements of the table row if this is not
								// the table header row
								switch (iCurrentSection)
								{
									case (int)E_RELNOTE_SECTION.FileAdd:
										if (-1 == strTableRow.IndexOf("Repository Source"))
										{
											ParseFileHTML(strTableRow);
										}
										break;
									case (int)E_RELNOTE_SECTION.RegistryAdd:
										if (-1 == strTableRow.IndexOf("<b>Key Name</b>"))
										{
											ParseRegistryHTML(strTableRow, E_REGPARSEOPTION.Add);
										}
										break;
									case (int)E_RELNOTE_SECTION.RegistryRemove:
										if (-1 == strTableRow.IndexOf("<b>Key Name</b>"))
										{
											ParseRegistryHTML(strTableRow, E_REGPARSEOPTION.Remove);
										}
										break;
									default:
										break;
								}
							
								// Reset the variables
								bInTableRow = false;
								strTableRow = "";
							}
							else
							{
								// We are still inside the table row, so append
								// the current line to the table row string
								strTableRow += strLine;
							}
						}
					}
					
					// Reset the section variable once we're done parsing the table
					iCurrentSection = (int)E_RELNOTE_SECTION.None;

				}
			}

			// Close the file stream
			sr.Close();

			return true;
		}


		private void ParseFileRTF(string strInputString)
		{
			// Check input value
			if ("" == strInputString)
				return;

			int iNumTokens = 0, iIndex = 0;
			string strSourceFilename = "", strDestFilename = "";

			// Tokenize the input string using tab delimiters
			string [] strTokens = strInputString.ToLower().Split('\t');

			// For file actions, there should be 2 tab-delimited substrings
			// of the form <source_filename>\t<destination_location><destination_filename>
			iNumTokens = strTokens.Length;

			// If there are < 2 tokens then bail out
			if (2 > iNumTokens)
				return;

			// Get the source and destination file names
			strSourceFilename = strTokens[0].Trim();
			strDestFilename = strTokens[1].Trim();

			// Trim the root directory off of the destination filename
			iIndex = strDestFilename.IndexOf(":");
			if ( (-1 != iIndex) && (strDestFilename.Length > iIndex) )
				strDestFilename = strDestFilename.Substring(iIndex+1);

			// Call the method to add this file to the list view
			ParseFileEntry(strSourceFilename, strDestFilename, "", true, (int)E_FILEOPERATION.Move, false);
		}


		private void ParseFileHTML(string strInputString)
		{
			// Check input value
			if ("" == strInputString)
				return;

			int iIndex = 0, iCloseIndex = 0;
			string strSourceFilename = "", strDestFilename = "";
			string strParsedInputString = strInputString.ToLower();

			// Look for table cells enclosed with <td> and </td>
			// For file entries there should be 2 of them
			iIndex = strParsedInputString.IndexOf("<td>");
			iCloseIndex = strParsedInputString.IndexOf("</td>");

			// If we did not find a table cell bail out
			if ( (-1 == iIndex) || (-1 == iCloseIndex) || (iCloseIndex <= iIndex) )
				return;

			// Get the source filename
			strSourceFilename = strParsedInputString.Substring(iIndex + 4, iCloseIndex - iIndex - 4);

			// Remove the source filename from the parsed string
			strParsedInputString = strParsedInputString.Substring(iCloseIndex + 5);

			// Look for table cells enclosed with <td> and </td>
			iIndex = strParsedInputString.IndexOf("<td>");
			iCloseIndex = strParsedInputString.IndexOf("</td>");

			// If we did not find a table cell bail out
			if ( (-1 == iIndex) || (-1 == iCloseIndex) || (iCloseIndex <= iIndex) )
				return;

			// Get the destination filename
			strDestFilename = strParsedInputString.Substring(iIndex + 4, iCloseIndex - iIndex - 4);

			// Trim the whitespace from the source and destination file names
			strSourceFilename = strSourceFilename.Trim();
			strDestFilename = strDestFilename.Trim();

			// Trim the root directory off of the destination filename
			iIndex = strDestFilename.IndexOf(":");
			if ( (-1 != iIndex) && (strDestFilename.Length > iIndex) )
				strDestFilename = strDestFilename.Substring(iIndex+1);

			// Call the method to add this file to the list view
			ParseFileEntry(strSourceFilename, strDestFilename, "", true, (int)E_FILEOPERATION.Move, false);
		}


		/*****************************************************************************************
		Function Name:	ParseRegistryRTF
		Author:			aaronste
		Description:	Takes a line of text representing an RTF formatted registry entry and
						parses it into its pieces and adds it to the registry list view UI
		Inputs:			string strInputString - string to parse
						E_REGPARSEOPTION eRegAction - whether the current regkey should be
							added or removed
		Outputs:		none
		******************************************************************************************/
		private void ParseRegistryRTF(string strInputString, E_REGPARSEOPTION eRegAction)
		{
			// Check input value
			if ("" == strInputString)
				return;

			string strRegRoot = "", strKeyName = "", strValueName = "", strValueData = "";
			int iRegValueDataType = (int)E_REGVALUEDATATYPE.REG_SZ;
			int iIndex, iNumTokens, iRegRootIndex;

			// Tokenize the input string using tab delimiters
			string [] strTokens = strInputString.Split('\t');

			// For registry actions, there should be 4 tab-delimited substrings
			// of the form <key_name>\t<value_name>\t<data_type>\t<value_data>
			iNumTokens = strTokens.Length;

			// Get the key name
			if (iNumTokens > 0)
				strKeyName = strTokens[0].Trim();

			// Split the registry root off of the front of the key name string
			iIndex = strKeyName.IndexOf("\\");
			if (-1 != iIndex)
			{
				// Grab the text before the first backslash
				strRegRoot = strKeyName.Substring(0, iIndex);

				// Convert it to the string format we expect
				// Some RTF files use abbreviations for registry root (such as HKCR), but
				// some use expanded form (such as HKEY_CLASSES_ROOT).  This logic needs to
				// account for both possibilities
				try
				{
					// A negative index here means that the registry root is not using
					// the abbreviated form, so attempt to use the expanded form instead
					iRegRootIndex = Array.IndexOf(g_strRegRootsShort, strRegRoot);
					if (-1 != iRegRootIndex)
					{
						strRegRoot = g_strRegRoots[Array.IndexOf(g_strRegRootsShort, strRegRoot)];
					}
					else
					{
						strRegRoot = g_strRegRoots[Array.IndexOf(g_strRegRoots, strRegRoot)];
					}
				}
				catch
				{
					// Default to HKLM if anything goes wrong
					strRegRoot = g_strRegRoots[(int)E_REGISTRYROOT.HKEY_LOCAL_MACHINE];
				}

				// Remove the reg root from the key name string
				if (strKeyName.Length > iIndex)
					strKeyName = strKeyName.Substring(iIndex+1);
				else
					strKeyName = "";
			}
			else
			{
				// If we fail to find the first backslash, then the registry data is in a format
				// that we do not expect, so we will skip processing this item and continue
				return;
			}

			// Remove trailing backslashes from the key name
			strKeyName = strKeyName.TrimEnd('\\');

			// Remove leading and trailing quotes from the key name
			strKeyName = strKeyName.TrimStart('"');
			strKeyName = strKeyName.TrimEnd('"');

			// For registry add operations, we need to grab the value name, data type and value data
			if (E_REGPARSEOPTION.Add == eRegAction)
			{
				// Get the value name, data type and value data
				if (iNumTokens > 1)
					strValueName = strTokens[1].Trim();

				if (iNumTokens > 2)
				{
					iRegValueDataType = Array.IndexOf(g_strRegValueDataTypes, strTokens[2].Trim());

					// If there is no data type given, default to REG_SZ
					if (-1 == iRegValueDataType)
						iRegValueDataType = (int)E_REGVALUEDATATYPE.REG_SZ;
				}

				if (iNumTokens > 3)
					strValueData = strTokens[3].Trim();

				// We may need to add commas for binary data - check here
				if (iRegValueDataType == (int)E_REGVALUEDATATYPE.REG_BINARY)
				{
					// If the reg value data is binary, and it is greater than 2
					// characters in length without any commas, then we will try
					// to parse it and insert commas after every 2 characters
					if ( (strValueData.Length > 2) && (-1 == strValueData.IndexOf(",")) )
					{
						string strDelimitedValueData = "";
						int iCount = 0;
						for (int i = 0; i < strValueData.Length; i++)
						{
							if (0 == iCount)
							{
								// Do not add a leading comma if this is the first item
								if (0 != i)
									strDelimitedValueData += ",";

								// Increment the counter
								iCount++;
							}
							else
							{
								// Reset the counter
								iCount = 0;
							}

							// Add the current character to the updated value data string
							strDelimitedValueData += strValueData[i];
						}

						// Update the value data that will be passed to the function below
						// after converting from hex to decimal
						strValueData = ConvertHexToDecimal(strDelimitedValueData);
					}
				}

				// Call the function to add this key to the list view UI
				AddRegistryEntryToListView(strRegRoot, strKeyName, strValueName, strValueData, iRegValueDataType, (int)E_REGISTRYOPERATION.CreateAndAddValue);
			}
			else if (E_REGPARSEOPTION.Remove == eRegAction)
			{
				// Get the value name, data type and value data
				if (iNumTokens > 1)
					strValueName = strTokens[1].Trim();

				// Call the function to add this key to the list view UI
				if ("" == strValueName)
				{
					AddRegistryEntryToListView(strRegRoot, strKeyName, "", "", 0, (int)E_REGISTRYOPERATION.DeleteKey);
				}
				else
				{
					AddRegistryEntryToListView(strRegRoot, strKeyName, strValueName, "", 0, (int)E_REGISTRYOPERATION.DeleteValue);
				}
			}
		}


		/*****************************************************************************************
		Function Name:	ParseRegistryHTML
		Author:			aaronste
		Description:	Takes a line of text representing an HTML table row that contains
						a registry value, parses it into its pieces and adds it to the 
						registry list view UI
		Inputs:			string strInputString - string to parse
						E_REGPARSEOPTION eRegAction - whether the current regkey should be
							added or removed
		Outputs:		none
		******************************************************************************************/
		private void ParseRegistryHTML(string strInputString, E_REGPARSEOPTION eRegAction)
		{
			// Check input value
			if ("" == strInputString)
				return;

			string strRegRoot = "", strRegKeyName = "", strRegValueName = "", strRegValueDataType = "", strRegValueData = "";
			string strParsedInputString = strInputString;
			int iRegValueDataType = (int)E_REGVALUEDATATYPE.REG_SZ;
			int iIndex, iCloseIndex;

			// Look for table cells enclosed with <td> and </td>
			// For registry entries there should be 4 of them
			iIndex = strParsedInputString.IndexOf("<td>");
			iCloseIndex = strParsedInputString.IndexOf("</td>");

			// If we did not find a table cell bail out
			if ( (-1 == iIndex) || (-1 == iCloseIndex) || (iCloseIndex <= iIndex) )
				return;

			// Get the registry key name
			strRegKeyName = strParsedInputString.Substring(iIndex + 4, iCloseIndex - iIndex - 4);

			// Remove the registry key name from the parsed string
			strParsedInputString = strParsedInputString.Substring(iCloseIndex + 5);

			// Look for table cells enclosed with <td> and </td>
			iIndex = strParsedInputString.IndexOf("<td>");
			iCloseIndex = strParsedInputString.IndexOf("</td>");

			// If we did not find a table cell bail out
			if ( (-1 == iIndex) || (-1 == iCloseIndex) || (iCloseIndex <= iIndex) )
				return;

			// Get the registry value name
			strRegValueName = strParsedInputString.Substring(iIndex + 4, iCloseIndex - iIndex - 4);

			// Remove the registry value name from the parsed string
			strParsedInputString = strParsedInputString.Substring(iCloseIndex + 5);

			// Look for table cells enclosed with <td> and </td>
			iIndex = strParsedInputString.IndexOf("<td>");
			iCloseIndex = strParsedInputString.IndexOf("</td>");

			// If we did not find a table cell bail out
			if ( (-1 == iIndex) || (-1 == iCloseIndex) || (iCloseIndex <= iIndex) )
				return;

			// Get the registry value data type
			strRegValueDataType = strParsedInputString.Substring(iIndex + 4, iCloseIndex - iIndex - 4);

			// Remove the registry value data type from the parsed string
			strParsedInputString = strParsedInputString.Substring(iCloseIndex + 5);

			// Look for table cells enclosed with <td> and </td>
			iIndex = strParsedInputString.IndexOf("<td>");
			iCloseIndex = strParsedInputString.IndexOf("</td>");

			// If we did not find a table cell bail out
			if ( (-1 == iIndex) || (-1 == iCloseIndex) || (iCloseIndex <= iIndex) )
				return;

			// Get the registry value data
			strRegValueData = strParsedInputString.Substring(iIndex + 4, iCloseIndex - iIndex - 4);

			// Trim the whitespace from the registry strings
			strRegKeyName = strRegKeyName.Trim();
			strRegValueName = strRegValueName.Trim();
			strRegValueDataType = strRegValueDataType.Trim();
			strRegValueData = strRegValueData.Trim();

			// Split the registry root off of the front of the key name string
			iIndex = strRegKeyName.IndexOf("\\");
			if (-1 != iIndex)
			{
				// Grab the text before the first backslash
				strRegRoot = strRegKeyName.Substring(0, iIndex);

				// Convert it to the string format we expect
				try
				{
					strRegRoot = g_strRegRoots[Array.IndexOf(g_strRegRootsShort, strRegRoot)];
				}
				catch
				{
					// Default to HKLM if anything goes wrong
					strRegRoot = g_strRegRoots[(int)E_REGISTRYROOT.HKEY_LOCAL_MACHINE];
				}

				// Remove the reg root from the key name string
				if (strRegKeyName.Length > iIndex)
					strRegKeyName = strRegKeyName.Substring(iIndex+1);
				else
					strRegKeyName = "";
			}

			// Remove trailing backslashes from the key name
			strRegKeyName = strRegKeyName.TrimEnd('\\');

			// Remove leading and trailing quotes from the key name
			strRegKeyName = strRegKeyName.TrimStart('"');
			strRegKeyName = strRegKeyName.TrimEnd('"');

			// For registry add operations, we need to grab the value name, data type and value data
			if (E_REGPARSEOPTION.Add == eRegAction)
			{
				iRegValueDataType = Array.IndexOf(g_strRegValueDataTypes, strRegValueDataType);

				// If there is no data type given, default to REG_SZ
				if (-1 == iRegValueDataType)
					iRegValueDataType = (int)E_REGVALUEDATATYPE.REG_SZ;

				// We may need to add commas for binary data - check here
				if (iRegValueDataType == (int)E_REGVALUEDATATYPE.REG_BINARY)
				{
					// If the reg value data is binary, and it is greater than 2
					// characters in length without any commas, then we will try
					// to parse it and insert commas after every 2 characters
					if ( (strRegValueData.Length > 2) && (-1 == strRegValueData.IndexOf(",")) )
					{
						string strDelimitedValueData = "";
						int iCount = 0;
						for (int i = 0; i < strRegValueData.Length; i++)
						{
							if (0 == iCount)
							{
								// Do not add a leading comma if this is the first item
								if (0 != i)
									strDelimitedValueData += ",";

								// Increment the counter
								iCount++;
							}
							else
							{
								// Reset the counter
								iCount = 0;
							}

							// Add the current character to the updated value data string
							strDelimitedValueData += strRegValueData[i];
						}

						// Update the value data that will be passed to the function below
						// after converting from hex to decimal
						strRegValueData = ConvertHexToDecimal(strDelimitedValueData);
					}
				}

				// Call the function to add this key to the list view UI
				AddRegistryEntryToListView(strRegRoot, strRegKeyName, strRegValueName, strRegValueData, iRegValueDataType, (int)E_REGISTRYOPERATION.CreateAndAddValue);
			}
			else if (E_REGPARSEOPTION.Remove == eRegAction)
			{
				// Call the function to add this key to the list view UI
				if ("" == strRegValueName)
				{
					AddRegistryEntryToListView(strRegRoot, strRegKeyName, "", "", 0, (int)E_REGISTRYOPERATION.DeleteKey);
				}
				else
				{
					AddRegistryEntryToListView(strRegRoot, strRegKeyName, strRegValueName, "", 0, (int)E_REGISTRYOPERATION.DeleteValue);
				}
			}
		}


		/*****************************************************************************************
		Function Name:	ParseRegistryFile
		Author:			aaronste
		Description:	Loops through a file, extracts registry key/value information and inserts
						it into the registry key item list
		Inputs:			string strFilename	- full path of the file to parse
		Outputs:		true if successful
						false if not
		******************************************************************************************/
		private bool ParseRegistryFile(string strFilename)
		{
			// Check input parameter
			if (null == strFilename)
				return false;

			// Make sure the input file exists
			if (false == System.IO.File.Exists(strFilename))
				return false;

			string strLine = "", strKey = "", strRegRoot = "";
			string strRegValue = "", strRegValueData = "", strRegValueDataLower = "";
			string strRegValueDataParsed = "";
			int iRegDataType = (int) E_REGVALUEDATATYPE.REG_SZ;
			bool bHaveKey = false;
			int iCheckEquals = 0;
			int iRegRootIndex = 0;
			char [] chTrimChar = {'0'};

			// Create a stream reader to parse the file
			StreamReader sr = new StreamReader(strFilename);
			while ((strLine = sr.ReadLine()) != null) 
			{
				strLine.Trim();
				if (strLine.Length > 0)
				{
					// Check to see if the line begins with an open brace
					// (which indicates a key name)
					if ('[' == strLine[0]) 
					{
						// Reset values from previous registry key we found
						bHaveKey = false;
						strRegRoot = "";
						strKey = "";

						// Retrieve the registry root and key name and validate the data
						iRegRootIndex = strLine.IndexOf("\\");
						if ( (-1 != iRegRootIndex) && (1 < iRegRootIndex) )
						{
							strRegRoot = strLine.Substring(1, iRegRootIndex-1);
							strKey = strLine.Substring(iRegRootIndex+1,strLine.Length-iRegRootIndex-2);

							if ( ("" != strRegRoot) && ("" != strKey) && (0 <= Array.IndexOf(g_strRegRoots, strRegRoot)) )
								bHaveKey = true;

						}
					}

					// Search for an equal sign in the current line
					iCheckEquals = strLine.IndexOf("=");

					// Insert the value into the registry key list view
					if ((-1 != iCheckEquals) && (bHaveKey))
					{
						// Determine the data type of this registry value by parsing the string
						strRegValue = StripQuotesFromString(strLine.Substring(0,iCheckEquals));
						strRegValueData = StripQuotesFromString(strLine.Substring(iCheckEquals+1));

						strRegValueDataLower = strRegValueData.ToLower();
						iRegDataType = (int) E_REGVALUEDATATYPE.REG_SZ;

						// .reg files use backslashes as special character delimiters
						// Need to parse and remove them here....
						strRegValueDataParsed = "";
						for (int i = 0; i < strRegValueData.Length; i++)
						{
							if ('\\' == strRegValueData[i])
							{
								// If a backslash is the last character in a line, we need to
								// retrieve the next line
								if (strRegValueData.Length == i+1)
								{
									if ((strLine = sr.ReadLine()) != null)
									{
										strRegValueData += strLine.Trim();
									}
								}
									// Need to check if the next character is also a backslash
								else if ('\\' == strRegValueData[i+1])
								{
									// Add a backslash to the updated string
									strRegValueDataParsed += "\\";

									// Skip over the 2nd backslash
									i++;
								}
							}
							else
							{
								// Add the current character to the updated string
								strRegValueDataParsed += strRegValueData[i];
							}
						}

						// TODO - need to figure out how to identify other registry data types
						// and implement the parsing logic here

						// For dword values we need to remove the leading 0's
						if (strRegValueDataLower.StartsWith("dword:"))
						{
							iRegDataType = (int) E_REGVALUEDATATYPE.REG_DWORD;
							strRegValueDataParsed = strRegValueDataParsed.Substring(strRegValueDataParsed.IndexOf("dword:") + 6);
							strRegValueDataParsed = strRegValueDataParsed.TrimStart(chTrimChar);
							if ("" == strRegValueDataParsed)
							{
								strRegValueDataParsed = "0";
							}
							else
							{
								// For non-zero values we need to convert
								// from hex to decimal to conform to DUA
								// rules for DWORD values
								strRegValueDataParsed = (Convert.ToInt16(strRegValueDataParsed, 16)).ToString();
							}
						}
						else if (strRegValueDataLower.StartsWith("hex:"))
						{
							strRegValueDataParsed = strRegValueDataParsed.Substring(strRegValueDataParsed.IndexOf("hex:") + 4);
							iRegDataType = (int) E_REGVALUEDATATYPE.REG_BINARY;

							// DUA expects binary data to be a comma-delimited list of decimal values
							// but .reg files store the data as hex values.  Convert the data here
							strRegValueDataParsed = ConvertHexToDecimal(strRegValueDataParsed);
						}
						else if (strRegValueDataLower.StartsWith("hex(2):"))
						{
							strRegValueDataParsed = strRegValueDataParsed.Substring(strRegValueDataParsed.IndexOf("hex(2):") + 7);
							iRegDataType = (int) E_REGVALUEDATATYPE.REG_EXPAND_SZ;
						}
						else if (strRegValueDataLower.StartsWith("hex(7):"))
						{
							strRegValueDataParsed = strRegValueDataParsed.Substring(strRegValueDataParsed.IndexOf("hex(7):") + 7);
							iRegDataType = (int) E_REGVALUEDATATYPE.REG_MULTI_SZ;
						}

						AddRegistryEntryToListView(strRegRoot,
							strKey,
							strRegValue,
							strRegValueDataParsed,
							iRegDataType,
							(int) E_REGISTRYOPERATION.CreateAndAddValue);
					}						
				}
			}

			// Close the file stream
			sr.Close();

			return true;
		}


		/*****************************************************************************************
		Function Name:	AddFileEntryToListView
		Author:			aaronste
		Description:	Adds the information to the file list view
		Inputs:			
		Outputs:		true if successful
						false if not
		******************************************************************************************/
		private void AddFileEntryToListView(string strShortSourceFilename,
			string strDestFolder,
			string strShortDestFilename,
			string strExecuteCmdLine,
			bool bCreateDirectory,
			int iFileOperation,
			bool bRebootRequired)
		{
			// Setup the DUAFile class information
			DUAFile dFile = new DUAFile();
			if (-1 == Array.IndexOf(g_strFileDefaultLocations, strDestFolder))
			{
				dFile.bCustomLocation = true;
				dFile.bCreateCustomLocation = bCreateDirectory;
			}
			dFile.Operation = iFileOperation;
			dFile.SourceFile = strShortSourceFilename;
			dFile.DestFile = strShortDestFilename;
			dFile.DestLocation = strDestFolder;
			dFile.ExecuteCommandLine = strExecuteCmdLine;
			dFile.bRebootRequired = bRebootRequired;

			// Add the file to the list view
			ListViewItem item = new ListViewItem();
			item.ImageIndex = 0;
			item.Text = strShortSourceFilename;
			item.SubItems.Add(g_strFileOperations[iFileOperation]);
			item.SubItems.Add(strDestFolder + "\\" + strShortDestFilename);
			item.Tag=dFile;
			listView1.Items.Add(item);
		}

		
		/*****************************************************************************************
		Function Name:	AddRegistryEntryToListView
		Author:			aaronste
		Description:	Loops through a file, extracts registry key/value information and inserts
						it into the registry key item list
		Inputs:			string strFilename	- full path of the file to parse
		Outputs:		true if successful
						false if not
		******************************************************************************************/
		private void AddRegistryEntryToListView(string strRegRoot,
			string strKeyName,
			string strValueName,
			string strValueData,
			int iRegValueDataType,
			int iRegistryOperation)
		{
			string strDisplayedRegValue = "";
			ListViewItem item = null;

			// Check input parameters
			if ( (iRegValueDataType < 0) && ((int)E_REGISTRYOPERATION.CreateAndAddValue == iRegistryOperation) )
				return;

			// setup the DUARegistryFile class info
			DUARegistryEntry dRegEntry = new DUARegistryEntry();

			dRegEntry.m_iRegRoot = Array.IndexOf(g_strRegRoots, strRegRoot);
			dRegEntry.m_strRegKeyName = strKeyName;
			dRegEntry.m_strRegValueName = strValueName;
			dRegEntry.m_strRegValueData = strValueData;
			dRegEntry.m_iRegValueDataType = iRegValueDataType;
			dRegEntry.m_iRegistryOperation = iRegistryOperation;

			item = new ListViewItem();
			item.ImageIndex = 0;
			item.Text = strRegRoot;
			item.SubItems.Add(strKeyName);
			if ("" != strValueName)
			{
				strDisplayedRegValue = strValueName;
				if ("" != strValueData)
					strDisplayedRegValue += "=" + strValueData;
			}
			item.SubItems.Add(strDisplayedRegValue);
			item.SubItems.Add(g_strRegistryOperations[iRegistryOperation]);
			item.Tag=dRegEntry;

			listView2.Items.Add(item);
		}


		private void button4_Click(object sender, System.EventArgs e)
		{
			GenerateDUAScript();
		}


		private void menuGenerateScript_Click(object sender, System.EventArgs e)
		{
			GenerateDUAScript();
		}

		
		/*****************************************************************************************
		Function Name:	GenerateDUAScript
		Author:			mikehall
		Description:	Shows a save file dialog to ask the user for the location to create 
						the DUS file, then creates the file
		Inputs:			none
		Outputs:		none
		******************************************************************************************/
		private void GenerateDUAScript()
		{
			// Grab the user settings from the registry
			if (false == UpdateUserSettings(false))
				return;

			SaveFileDialog saveFileDialog1 = new SaveFileDialog();
			saveFileDialog1.FileName = "";
			saveFileDialog1.Filter = "DUA Script|*.dus";
			saveFileDialog1.Title = "Save a DUA Script File";
			saveFileDialog1.ShowDialog();

			// If the file name is not an empty call the method to generate the file
			if(saveFileDialog1.FileName != "") 
			{
				GenerateDUAScript(saveFileDialog1.FileName, false);
			}
		}


		/*****************************************************************************************
		Function Name:	GenerateDUAScript
		Author:			mikehall
		Description:	Processes all file and registry entries and writes them to a DUS file
		Inputs:			strFileName - full path to the DUS file to be created
						bSilent - whether or not DUAScriptGen is running in silent mode
		Outputs:		none
		******************************************************************************************/
		private void GenerateDUAScript(string strFileName, bool bSilent)
		{
			// Check input parameter
			if ("" == strFileName)
				return;

			// Set tracking variables
			bool bRebootRequiredFilesInUse = false;

			// Grab the user settings from the registry
			if (false == UpdateUserSettings(bSilent))
				return;

			g_strSavePath = strFileName;
			int iIndex = g_strSavePath.LastIndexOf("\\");
			if (-1 != iIndex) 
			{
				strSourceFile = strFileName;
				g_strSavePath = g_strSavePath.Substring(0,iIndex);	// get the path...
				DUA_OutputFolder = g_strSavePath + "\\DUA_Files";

				g_strSaveFile = strFileName.Substring(iIndex+1,strFileName.Length-iIndex-1);	// get the file.
				iIndex = g_strSaveFile.LastIndexOf(".");
				g_strSaveFile = g_strSaveFile.Substring(0,iIndex);
				g_strSaveFile = DUA_OutputFolder + "\\" + g_strSaveFile + ".dup";
			}
			
			// Delete the DUS file if it already exists
			System.IO.File.Delete(strFileName);
			
			StreamWriter sw = new StreamWriter(strFileName);
			sw.WriteLine(g_strDuaScriptGenHeader);
			sw.WriteLine(g_strCopyright);
			sw.WriteLine("");
			int iCount = listView1.Items.Count;

			// TODO - Write commands to disable EWF if applicable
			/*
			// Disable EWF
			EXECUTEPROCESS,,,,c:\windows\system32\ewfmgr.exe,0,c: -disable,DAYES,60000,,DASEC_REV,,,,DASEC_REV,,,,,, c:\windows\system32,1,,WinSta0\Default

			// Shut down and restart the system
			REBOOT,,DAREBOOTOPT_REBOOT
			*/

			// Loop through all of the file entries in the list view and
			// perform the chosen retrieval (HTTP get or copy from share)
			// We want to do all of the file copies first, then any 
			// necessary move/execute operations
			DUAFile dFile;
			int x;
			for(x = 0; x < iCount; x++) 
			{
				dFile = (DUAFile)listView1.Items[x].Tag;
				
				// We only need to transfer a file if the operation is one of
				// the following: copy, move, copy & execute, move & execute
				if ( ((int)E_FILEOPERATION.Delete != dFile.Operation) && ((int)E_FILEOPERATION.Execute != dFile.Operation) )
					WriteFileTransferToDusFile(dFile, sw);
			}

			// Write a delimiter for the beginning of the file section
			sw.WriteLine("");
			sw.WriteLine(g_strFileSectionBegin);

			// Loop through all of the file entries in the list view and 
			// add move/execute actions to the DUS file
			for(x = 0; x < iCount; x++) 
			{
				dFile = (DUAFile)listView1.Items[x].Tag;
				WriteFileEntryToDusFile(dFile, sw);

				// Check to see if we need to write post-reboot deletions later on
				if (true == dFile.bRebootRequired)
					bRebootRequiredFilesInUse = true;
			}

			// Write a delimiter for the end of the file section
			sw.WriteLine("");
			sw.WriteLine(g_strFileSectionEnd);

			// Write a delimiter for the beginning of the registry section
			sw.WriteLine("");
			sw.WriteLine(g_strRegistrySectionBegin);

			// Loop through all of the registry entries in the list view and 
			// add them to the DUS file
			iCount = listView2.Items.Count;
			DUARegistryEntry dEntry;
			for(x = 0; x < iCount; x++) 
			{
				dEntry = (DUARegistryEntry)listView2.Items[x].Tag;
				WriteRegistryEntryToDusFile(dEntry, sw);
			}

			// Write a delimiter for the end of the registry section
			sw.WriteLine("");
			sw.WriteLine(g_strRegistrySectionEnd);

			// Check to see if we have a next script name, and if so
			// write the registry entry for it
			if ("" != g_strNextScriptName)
			{
				// Set up the DUARegistryEntry data structure
				dEntry = new DUARegistryEntry();
				dEntry.m_iRegistryOperation = (int)E_REGISTRYOPERATION.CreateAndAddValue;
				dEntry.m_iRegRoot = (int)E_REGISTRYROOT.HKEY_LOCAL_MACHINE;
				dEntry.m_strRegKeyName = "SYSTEM\\CurrentControlSet\\Services\\DUAgent\\Parameters\\Config\\Sessions\\0000";
				dEntry.m_strRegValueName = "CmdFile";

				// Check to see if we are doing HTTP polling or share polling
				if (1 != g_iUseHTTPPolling)
				{
					// If we are doing share polling, append the share name to
					// the next DUP file name
					dEntry.m_strRegValueData = g_strDUAServerPath + "\\" + g_strNextScriptName;
				}
				else
				{
					dEntry.m_strRegValueData = g_strNextScriptName;
				}
				
				dEntry.m_iRegValueDataType = (int)E_REGVALUEDATATYPE.REG_SZ;
				WriteRegistryEntryToDusFile(dEntry, sw);
			}

			// Check to see if we should reboot at the end of the script
			if ( (1 == g_iRebootAfterScript) || (true == bRebootRequiredFilesInUse) )
			{
				sw.WriteLine("");
				sw.WriteLine("REBOOT,,DAREBOOTOPT_REBOOT");
			}

			// Check to see if we need to write any post-reboot file deletions
			if (bRebootRequiredFilesInUse)
			{
				// Write a delimiter for the beginning of the post-reboot file execution section
				sw.WriteLine("");
				sw.WriteLine(g_strPostRebootExecuteSectionBegin);
				sw.WriteLine("");

				// Loop through all of the file entries in the list view and 
				// add move/execute actions to the DUS file
				iCount = listView1.Items.Count;
				for(x = 0; x < iCount; x++) 
				{
					dFile = (DUAFile)listView1.Items[x].Tag;

					if (true == dFile.bRebootRequired)
					{
						// Write the renamed file deletion to the DUS file if appropriate
						WritePostRebootFileDeletionToDusFile(dFile, sw);

						// Write the post-reboot file execution operation if appropriate
						if ((int)E_FILEOPERATION.CopyAndExecute == dFile.Operation)
						{
							WritePostRebootExecuteEntryToDusFile(dFile, sw);
						}
					}
				}

				// Write a delimiter for the end of the post-reboot file execution section
				sw.WriteLine("");
				sw.WriteLine(g_strPostRebootExecuteSectionEnd);
			}

			// TODO - Write commands to re-enable EWF if applicable
			/*
			// Re-enable EWF
			EXECUTEPROCESS,,,,c:\windows\system32\ewfmgr.exe,0,c: -enable,DAYES,60000,,DASEC_REV,,,,DASEC_REV,,,,,, c:\windows\system32,1,,WinSta0\Default

			// Shut down and restart the system
			REBOOT,,DAREBOOTOPT_REBOOT
			*/

			// Write a comment indicating the version of DUAScriptGen we used to create the file
			sw.WriteLine("");
			sw.WriteLine("// Created by DUAScriptGen " + g_strVersionString);

			sw.Close( );
		}


		private bool WriteFileTransferToDusFile(DUAFile dFile, StreamWriter sw)
		{
			// First check to see if we are doing HTTP polling or share polling
			if (1 == g_iUseHTTPPolling)
			{
				// Determine the server name and path from the
				// DUA server global variable
				string strServerName = g_strDUAServerPath;
				string strServerRelativePath = "";
				int iIndex = g_strDUAServerPath.IndexOf("/");

				if (-1 != iIndex)
				{
					strServerName = g_strDUAServerPath.Substring(0, iIndex);
					
					if (g_strDUAServerPath.Length > iIndex)
						strServerRelativePath = g_strDUAServerPath.Substring(iIndex+1);
					
					// Add a trailing forward slash if there isn't already one
					if (strServerRelativePath.LastIndexOf('/') != strServerRelativePath.Length-1)
						strServerRelativePath += "/";
				}

				// Write the HTTPGET command to the DUS file
				sw.WriteLine("HTTPGET,0,0," + strServerName + ",0," + strServerRelativePath + dFile.SourceFile + ",0," + g_strLocalDrive + "\\" + dFile.SourceFile);
			}
			else
			{
				// Write a COPY command to the DUS file if the server path and local drive
				// are not the same
				if (g_strDUAServerPath.ToLower() != g_strLocalDrive.ToLower())
					sw.WriteLine("COPYFILE,,," + g_strDUAServerPath + "\\" + dFile.SourceFile + ",," + g_strLocalDrive + "\\" + dFile.SourceFile + ",DANO");
			}

			return true;
		}


		private bool WriteFileEntryToDusFile(DUAFile dFile, StreamWriter sw)
		{
			string strSourceLocation = "";
			string strLocalDriveLetter = "";
			string strDestDirectory = "", strDestFileLocation = "";
			int iIndex;

			// When we write these entries, we have already copied the file
			// from the polling location to the local DUA working directory or
			// we are polling directly from the local DUA working directory.
			strSourceLocation = g_strLocalDrive + "\\" + dFile.SourceFile;

			// Check to see if the destination location already has a drive letter by
			// checking for a : in the string
			iIndex = dFile.DestLocation.IndexOf(":");
			if (-1 == iIndex)
			{
				// If there is not already a drive letter on the destination location, find
				// the root of the local DUA drive by removing the drive letter from
				// the DUA local drive
				iIndex = g_strLocalDrive.IndexOf(":");
				if (-1 == iIndex)
					return false;

				strLocalDriveLetter = g_strLocalDrive.Substring(0, iIndex+1);

				// Now build the destination directory using this
				strDestDirectory = strLocalDriveLetter + dFile.DestLocation;
			}
			else
			{
				// If there is already a drive letter, build the destination directory
				// without prepending a local drive letter
				strDestDirectory = dFile.DestLocation;
			}

			// Create the destination file location string
			strDestFileLocation = strDestDirectory + "\\" + dFile.DestFile;

			// Write the move action to the DUS file
			if ( ((int)E_FILEOPERATION.Move == dFile.Operation) || ((int)E_FILEOPERATION.MoveAndExecute == dFile.Operation) )
			{
				sw.WriteLine("");
				sw.WriteLine("// ***********************************************************");
				sw.WriteLine("// MOVEFILE");
				sw.WriteLine("// Error Mode");
				sw.WriteLine("// Expand Mode");
				sw.WriteLine("// Source - " + strSourceLocation);
				sw.WriteLine("// Expand");									
				sw.WriteLine("// Destination - " + strDestFileLocation);

				// Write the create directory action if applicable
				if (true == dFile.bCreateCustomLocation)
				{
					sw.WriteLine("CREATEDIRECTORY,,," + strDestDirectory);
				}

				sw.WriteLine("MOVEFILE,,," + strSourceLocation + ",," + strDestFileLocation + ",DAMOVEFILE_REPLACE_EXISTING");
			}

			// Write the copy action to the DUS file
			if ( ((int)E_FILEOPERATION.Copy == dFile.Operation) || ((int)E_FILEOPERATION.CopyAndExecute == dFile.Operation) )
			{
				// Check to see if a reboot is needed for this file
				if (false == dFile.bRebootRequired)
				{
					sw.WriteLine("");
					sw.WriteLine("// ***********************************************************");
					sw.WriteLine("// COPYFILE");
					sw.WriteLine("// Error Mode");
					sw.WriteLine("// Expand Mode");
					sw.WriteLine("// Source - " + strSourceLocation);
					sw.WriteLine("// Expand");									
					sw.WriteLine("// Destination - " + strDestFileLocation);
					sw.WriteLine("// FailIfExists - DANO, overwrite existing file");

					// Write the create directory action if applicable
					if (true == dFile.bCreateCustomLocation)
					{
						sw.WriteLine("CREATEDIRECTORY,,," + strDestDirectory);
					}
					sw.WriteLine("COPYFILE,,," + strSourceLocation + ",," + strDestFileLocation + ",DANO");
				}
				else
				{
					sw.WriteLine("");
					sw.WriteLine("// ***********************************************************");
					sw.WriteLine("// MOVEFILE - reboot needed because file may be in use");
					sw.WriteLine("// Error Mode");
					sw.WriteLine("// Expand Mode");
					sw.WriteLine("// Source - " + strSourceLocation);
					sw.WriteLine("// Expand");									
					sw.WriteLine("// Destination - " + strDestFileLocation);

					// Write the create directory action if applicable
					if (true == dFile.bCreateCustomLocation)
					{
						sw.WriteLine("CREATEDIRECTORY,,," + strDestDirectory);
					}

					// Rename the target file
					sw.WriteLine("MOVEFILE,,," + strDestFileLocation + ",," + strDestFileLocation + g_strFileInUseRenameExtension + ",DAMOVEFILE_REPLACE_EXISTING");

					// Move the new version into the target location
					sw.WriteLine("MOVEFILE,,," + strSourceLocation + ",," + strDestFileLocation + ",DAMOVEFILE_REPLACE_EXISTING");
				}
			}

			// Write the execute action to the DUS file unless a reboot is required
			if ( (false == dFile.bRebootRequired) && (((int)E_FILEOPERATION.MoveAndExecute == dFile.Operation) || ((int)E_FILEOPERATION.CopyAndExecute == dFile.Operation) || ((int)E_FILEOPERATION.Execute == dFile.Operation)) )
			{
				// Only write the header info for the standalone executeprocess command to make
				// the logic for reading back in a DUS file easy
				if ((int)E_FILEOPERATION.Execute == dFile.Operation)
				{
					sw.WriteLine("");
					sw.WriteLine("// ***********************************************************");
					sw.WriteLine("// EXECUTEPROCESS");
					sw.WriteLine("// Source - " + strDestFileLocation);
					sw.WriteLine("// Command line parameters - " + dFile.ExecuteCommandLine);
					sw.WriteLine("// Wait on exit - yes");									
					sw.WriteLine("// Wait time - 4294967295 (infinite)");
					sw.WriteLine("// Working directory - " + strDestDirectory);
				}

				sw.WriteLine("EXECUTEPROCESS,,,," + strDestFileLocation + ",," + dFile.ExecuteCommandLine + ",DAYES,4294967295,,DASEC_REV,,,,DASEC_REV,,,,,," + strDestDirectory + ",1,,WinSta0\\Default");
			}

			// Write the delete action to the DUS file
			if ((int)E_FILEOPERATION.Delete == dFile.Operation)
			{
				// Check to see if a reboot is needed for this file
				if (false == dFile.bRebootRequired)
				{
					sw.WriteLine("");
					sw.WriteLine("// ***********************************************************");
					sw.WriteLine("// DELETEFILE");
					sw.WriteLine("// File name - " + strDestFileLocation);
					sw.WriteLine("DELETEFILE,,," + strDestFileLocation);
				}
				else
				{
					sw.WriteLine("");
					sw.WriteLine("// ***********************************************************");
					sw.WriteLine("// MOVEFILE - reboot needed because file may be in use");
					sw.WriteLine("// Error Mode");
					sw.WriteLine("// Expand Mode");
					sw.WriteLine("// Source - " + strSourceLocation);
					sw.WriteLine("// Expand");									
					sw.WriteLine("// Destination - " + strDestFileLocation);

					// Rename the target file
					sw.WriteLine("MOVEFILE,,," + strDestFileLocation + ",," + strDestFileLocation + g_strFileInUseRenameExtension + ",DAMOVEFILE_REPLACE_EXISTING");
				}
			}

			return true;
		}


		private bool WritePostRebootExecuteEntryToDusFile(DUAFile dFile, StreamWriter sw)
		{
			string strLocalDriveLetter = "";
			string strDestDirectory = "", strDestFileLocation = "";
			int iIndex;

			// Check to see if the destination location already has a drive letter by
			// checking for a : in the string
			iIndex = dFile.DestLocation.IndexOf(":");
			if (-1 == iIndex)
			{
				// If there is not already a drive letter on the destination location, find
				// the root of the local DUA drive by removing the drive letter from
				// the DUA local drive
				iIndex = g_strLocalDrive.IndexOf(":");
				if (-1 == iIndex)
					return false;

				strLocalDriveLetter = g_strLocalDrive.Substring(0, iIndex+1);

				// Now build the destination directory using this
				strDestDirectory = strLocalDriveLetter + dFile.DestLocation;
			}
			else
			{
				// If there is already a drive letter, build the destination directory
				// without prepending a local drive letter
				strDestDirectory = dFile.DestLocation;
			}

			// Create the destination file location string
			strDestFileLocation = strDestDirectory + "\\" + dFile.DestFile;

			// Write the execute action to the DUS file if a reboot is required
			sw.WriteLine("EXECUTEPROCESS,,,," + strDestFileLocation + ",," + dFile.ExecuteCommandLine + ",DAYES,4294967295,,DASEC_REV,,,,DASEC_REV,,,,,," + strDestDirectory + ",1,,WinSta0\\Default");

			return true;
		}


		private bool WritePostRebootFileDeletionToDusFile(DUAFile dFile, StreamWriter sw)
		{
			string strLocalDriveLetter = "";
			string strDestDirectory = "", strDestFileLocation = "";
			int iIndex;

			// Check to see if the destination location already has a drive letter by
			// checking for a : in the string
			iIndex = dFile.DestLocation.IndexOf(":");
			if (-1 == iIndex)
			{
				// If there is not already a drive letter on the destination location, find
				// the root of the local DUA drive by removing the drive letter from
				// the DUA local drive
				iIndex = g_strLocalDrive.IndexOf(":");
				if (-1 == iIndex)
					return false;

				strLocalDriveLetter = g_strLocalDrive.Substring(0, iIndex+1);

				// Now build the destination directory using this
				strDestDirectory = strLocalDriveLetter + dFile.DestLocation;
			}
			else
			{
				// If there is already a drive letter, build the destination directory
				// without prepending a local drive letter
				strDestDirectory = dFile.DestLocation;
			}

			// Create the destination file location string
			strDestFileLocation = strDestDirectory + "\\" + dFile.DestFile;

			// Write the deletion command to the DUS file
			sw.WriteLine("DELETEFILE,,," + strDestFileLocation + g_strFileInUseRenameExtension);

			return true;
		}


		private bool WriteRegistryEntryToDusFile(DUARegistryEntry dEntry, StreamWriter sw)
		{
			bool bRetValue = false;
			int iCommaCount = 0;
			string strRegValueData = "";

			// As an extra safeguard, make sure we have a valid index for the registry root
			if ( (dEntry.m_iRegRoot < 0) || (dEntry.m_iRegRoot >= g_strRegRoots.Length) )
				return false;

			// Due to XPe bug 9641, the DUA script compiler does not accept DAREG_DWORD as
			// a registry value data type.  Check for this and convert the string to 
			// DAREG_DWORD_LITTLE_ENDIAN if needed
			string strRegValueDataType = "DA" + g_strRegValueDataTypes[dEntry.m_iRegValueDataType];
			if (dEntry.m_iRegValueDataType == (int)E_REGVALUEDATATYPE.REG_DWORD)
				strRegValueDataType += "_LITTLE_ENDIAN";

			// Need to compute a size value for certain reg value data types
			string strRegValueSize = "";
			if ( (dEntry.m_iRegValueDataType == (int)E_REGVALUEDATATYPE.REG_BINARY) ||
				(dEntry.m_iRegValueDataType == (int)E_REGVALUEDATATYPE.REG_MULTI_SZ) )
			{
				// Figure out the number of commas in the reg value data and add 1 to get
				// what we need for the size value
				foreach (char c in dEntry.m_strRegValueData)
				{
					if (',' == c)
						iCommaCount++;
				}

				// Update the size value - note that the number of commas is one
				// less than the number of actual data items
				iCommaCount++;
				strRegValueSize = Convert.ToString(iCommaCount);
			}

			// Write the appropriate DUS file registry line based on the operation specified
			// in the passed in DUARegistryEntry data structure
			switch (dEntry.m_iRegistryOperation)
			{
				case ((int)E_REGISTRYOPERATION.Create):
					// Write the line to create the key
					sw.WriteLine("");
					sw.WriteLine("REGCREATEKEY,," + g_strRegRoots[dEntry.m_iRegRoot] + 
						",," + dEntry.m_strRegKeyName +
						",DAREG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS");

					break;

				case ((int)E_REGISTRYOPERATION.CreateAndAddValue):
					// Write the line to create the key
					sw.WriteLine("");
					sw.WriteLine("REGCREATEKEY,," + g_strRegRoots[dEntry.m_iRegRoot] + 
						",," + dEntry.m_strRegKeyName +
						",DAREG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS");

					// Set the value to a blank space if there is no value data present
					if ("" != dEntry.m_strRegValueData)
					{
						strRegValueData = dEntry.m_strRegValueData;

						// REG_SZ values can have file tokens in them so we need to
						// check for the data value type and then replace tokens if appr
						if ((int)E_REGVALUEDATATYPE.REG_SZ == dEntry.m_iRegValueDataType)
							strRegValueData = SubstituteForTokens(strRegValueData);
					}
					else
					{
						strRegValueData = " ";
					}

					// Write the line to set the value
					// Need to check to see if a size value is required for the current
					// key's data type
					if (true == IsRegistryDataSizeParameterRequired(dEntry.m_iRegValueDataType))
						sw.WriteLine("REGSETVALUE,," + g_strRegRoots[dEntry.m_iRegRoot] +
							",," + dEntry.m_strRegKeyName + ",," +
							dEntry.m_strRegValueName + "," + strRegValueDataType +
							"," + strRegValueSize + "," + strRegValueData);
					else
						sw.WriteLine("REGSETVALUE,," + g_strRegRoots[dEntry.m_iRegRoot] +
							",," + dEntry.m_strRegKeyName + ",," +
							dEntry.m_strRegValueName + "," + strRegValueDataType +
							"," + strRegValueData);
				
					break;

				case ((int)E_REGISTRYOPERATION.DeleteKey):
					// Write the line to delete the key
					sw.WriteLine("");
					sw.WriteLine("REGDELETEKEY,," + g_strRegRoots[dEntry.m_iRegRoot] + 
						",," + dEntry.m_strRegKeyName);

					break;

				case ((int)E_REGISTRYOPERATION.DeleteValue):
					// Write the line to delete the value
					sw.WriteLine("");
					sw.WriteLine("REGDELETEVALUE,," + g_strRegRoots[dEntry.m_iRegRoot] +
						",," + dEntry.m_strRegKeyName + ",," +
						dEntry.m_strRegValueName);

					break;

				case ((int)E_REGISTRYOPERATION.RestoreKey):
					// TODO - implement this later
					break;

				case ((int)E_REGISTRYOPERATION.SaveKey):
					// TODO - implement this later
					break;

				default:
					break;
			}

			return bRetValue;
		}

		string RemoveDoubleSlash(string strTemp)
		{
			string strKey=strTemp;
			int iIndex=0;

			while(true) 
			{
				iIndex=strKey.IndexOf("\\\\");
				if (-1 == iIndex) 
				{
					break;
				} 
				else 
				{
					strKey=strKey.Substring(0,iIndex)+strKey.Substring(iIndex+1,strKey.Length-iIndex-1);
				}
			}
			return strKey;
		}

		string StripQuotesFromString(string strKey)
		{
			// Check for a null input value or a string < 2 characters in length
			if ( ("" == strKey) || (strKey.Length < 2) )
				return strKey;

			string strReturnValue = strKey;
			
			// Remove white space
			strReturnValue = strReturnValue.Trim();

			// This function will only remove leading and trailing quotes to allow
			// for the possibility of quotes within the string itself
			if ( ('\"' == strReturnValue[0]) && ('\"' == strReturnValue[strReturnValue.Length-1]) )
				strReturnValue = strReturnValue.Substring(1, strReturnValue.Length - 2);

			return strReturnValue;
		}

		private void File_Exit_Click(object sender, System.EventArgs e)
		{
			this.Close( );
		}


		private void Tools_Options_Click(object sender, System.EventArgs e)
		{
			Tool_Options Options = new Tool_Options();
			Options.ShowDialog();
		}


		/*****************************************************************************************
		Function Name:	UpdateUserSettings
		Author:			aaronste
		Description:	Reads the DUAScriptGen registry values and stores them in global
						variables for use in DUS file writing functions
		Inputs:			bSilent - whether or not DUAScriptGen is running in silent mode
		Outputs:		true if successful, false otherwise
		Notes:			There are safeguards elsewhere in this program to ensure that the user
						has registry data for all of these values.  Any error conditions due to
						missing registry values are extreme corner cases
		******************************************************************************************/
		private bool UpdateUserSettings(bool bSilent)
		{
			bool bRetValue = true;

			// Set default values to be used when creating the DUS file in case we are in silent
			// mode and the registry values do not exist
			g_iUseHTTPPolling = 1;
			g_strDUAServerPath = "server/share";
			g_strLocalDrive = "c:\\dua";
			g_strNextScriptName = "script2.dup";
			g_iRebootAfterScript = 1;

			// Try to open the DUAScriptGen settings registry key
            Microsoft.Win32.RegistryKey keyDUAScriptGen = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE\\DUAScriptGen");
			if (null == keyDUAScriptGen)
			{
				// Only show the UI if we are not in silent mode
				if (false == bSilent)
				{
					// If the DUAScriptGen settings registry key does not exist, show the options
					// dialog when the user first launches the tool
					Tool_Options Options = new Tool_Options();
					
					// If the user cancels out of the settings dialog, return
					// false because they don't have the required settings
					if (DialogResult.Cancel == Options.ShowDialog())
						bRetValue = false;
				}
			}
			else 
			{
				// If the DUAScriptGen settings registry key does exist, make sure it has all
				// of the values we're expecting
				if ((null == keyDUAScriptGen.GetValue("UseHTTPPolling")) ||
					(null == keyDUAScriptGen.GetValue("DUAServerPath")) ||
					(null == keyDUAScriptGen.GetValue("LocalDrive")) ||
					(null == keyDUAScriptGen.GetValue("NextScriptName")) ||
					(null == keyDUAScriptGen.GetValue("RebootAfterScript")) )
				{
					// Only show the UI if we are not in silent mode
					if (false == bSilent)
					{
						// If any of the values do not exist, show the options dialog
						Tool_Options Options = new Tool_Options();
						
						// If the user cancels out of the settings dialog, return
						// false because they don't have the required settings
						if (DialogResult.Cancel == Options.ShowDialog())
							bRetValue = false;
					}
				}
				else
				{
					g_iUseHTTPPolling = (int)keyDUAScriptGen.GetValue("UseHTTPPolling");
					g_strDUAServerPath = (string)keyDUAScriptGen.GetValue("DUAServerPath");
					g_strLocalDrive = (string)keyDUAScriptGen.GetValue("LocalDrive");
					g_strNextScriptName = (string)keyDUAScriptGen.GetValue("NextScriptName");
					g_iRebootAfterScript = (int)keyDUAScriptGen.GetValue("RebootAfterScript");
				}
			}

			return bRetValue;
		}

		private void AddFilesButton_Click(object sender, System.EventArgs e)
		{
			openFileDialog1.FileName = "";
			openFileDialog1.DefaultExt = "";
			openFileDialog1.Filter = "All files (*.*)|*.*";
			openFileDialog1.Multiselect=true;

			// OK button was pressed
			if (DialogResult.OK == openFileDialog1.ShowDialog()) 
			{
				foreach(string s in openFileDialog1.FileNames)
				{
					// Pull the file information and add to the list view
					ParseFileEntry(s, "", "", true, (int)E_FILEOPERATION.Move, false);
				}
			}
		}

		private void button1_Click(object sender, System.EventArgs e)
		{
			ListView.SelectedIndexCollection SelectedIndexes=listView1.SelectedIndices;
			int iCount=SelectedIndexes.Count;

			if (iCount > 0) 
			{
				for(int x=iCount-1;x > -1;x--) // delete bottom up...
				{
					listView1.Items.RemoveAt(SelectedIndexes[x]);
				}
			}
		}

		// Edit an existing item - need to extract the DUAFile class and update this...
		private void EditItem_Click(object sender, System.EventArgs e)
		{
			ListView.SelectedIndexCollection SelectedIndexes=listView1.SelectedIndices;
			int iCount=SelectedIndexes.Count;

			if (iCount == 1) 
			{
				GoEditItem(SelectedIndexes[0]);
			}
			else 
			{
				MessageBox.Show("Please select exactly one file to edit");
			}
		}


		void GoEditItem(int iSelectedItem)
		{
			// Create the file edit dialog and set the initial data
			EditItem eItem = new EditItem();
			eItem.dFile = (DUAFile)listView1.Items[iSelectedItem].Tag;

			// Show the file edit dialog
			eItem.ShowDialog();

			// Update the data and UI if the user pressed OK on the dialog
			if (DialogResult.OK == eItem.DialogResult) 
			{
				// Remove trailing backslashes from the destination folder name
				eItem.dFile.DestLocation = eItem.dFile.DestLocation.TrimEnd('\\');

				// Update list view items based on the user selections
				listView1.Items[iSelectedItem].Tag = eItem.dFile;
				listView1.Items[iSelectedItem].SubItems[1].Text = g_strFileOperations[eItem.dFile.Operation];
				listView1.Items[iSelectedItem].SubItems[2].Text = eItem.dFile.DestLocation + "\\" + eItem.dFile.DestFile;
			}
		}


		private void Form1_Load(object sender, System.EventArgs e)
		{
			this.CenterToScreen();

			// Try to open the DUAScriptGen settings registry key
            Microsoft.Win32.RegistryKey keyDUAScriptGen = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE\\DUAScriptGen");
			if (null == keyDUAScriptGen)
			{
				// If the DUAScriptGen settings registry key does not exist, show the options
				// dialog when the user first launches the tool
				Tool_Options Options = new Tool_Options();
				
				// If the user cancels out of the initial settings dialog, exit
				// the application because they don't have the required settings
				if (DialogResult.Cancel == Options.ShowDialog())
					this.Close();
			}
			else 
			{
				// If the DUAScriptGen settings registry key does exist, make sure it has all
				// of the values we're expecting
				if ((null == keyDUAScriptGen.GetValue("UseHTTPPolling")) ||
					(null == keyDUAScriptGen.GetValue("DUAServerPath")) ||
					(null == keyDUAScriptGen.GetValue("LocalDrive")) ||
					(null == keyDUAScriptGen.GetValue("NextScriptName")) ||
					(null == keyDUAScriptGen.GetValue("RebootAfterScript")) )
				{
					// If any of the values do not exist, show the options dialog
					Tool_Options Options = new Tool_Options();
					
					// If the user cancels out of the initial settings dialog, exit
					// the application because they don't have the required settings
					if (DialogResult.Cancel == Options.ShowDialog())
						this.Close();
				}
			}

			// Update the global variables with the chosen user settings
			UpdateUserSettings(false);

			// Build the path to Inf2Rtf.exe
			BuildInf2RtfPath();

			// Check to see if inf2rtf.exe exists in the same path as duascriptgen.exe
			if (!File.Exists(g_strInf2RtfPath))
			{
				// If the file does not exist, hide the options to parse INF files
				// because that functionality requires this file
				btnParseInf.Visible = false;
				menuConvertInf.Visible = false;
			}
		}

		private void BuildInf2RtfPath()
		{
			// Get the path that duascriptgen.exe is running from
			g_strInf2RtfPath = Application.ExecutablePath;

			// Remove the file name
			int iIndex = g_strInf2RtfPath.LastIndexOf('\\');
			if (-1 != iIndex)
			{
				g_strInf2RtfPath = g_strInf2RtfPath.Substring(0, iIndex);
			}

			// Add the filename for inf2rtf.exe
			g_strInf2RtfPath += "\\inf2rtf.exe";
		}

		private void ExecuteCompiler_Click(object sender, System.EventArgs e)
		{
			ExecuteDuscCompiler();
		}


		private void menuExecuteCompiler_Click(object sender, System.EventArgs e)
		{
			ExecuteDuscCompiler();
		}


		/*****************************************************************************************
		Function Name:	ExecuteDuscCompiler
		Author:			aaronste
		Description:	Attempts to compile the DUS file and shows UI to the user indicating
						success or failure
		Inputs:			none
		Outputs:		none
		******************************************************************************************/
		private void ExecuteDuscCompiler()
		{
			string strDuscPath = "";

			// Find the Windows Embedded root directory using the registry
			Microsoft.Win32.RegistryKey keyEmbeddedPath = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows Embedded");
			if (null != keyEmbeddedPath)
			{
				if (null != keyEmbeddedPath.GetValue("InstallDir"))
				{
					strDuscPath = keyEmbeddedPath.GetValue("InstallDir") + "bin\\dusc.exe";
				}
			}

			// Show an error dialog if the user does not have Windows Embedded installed
			if ( ("" == strDuscPath) || (!File.Exists(strDuscPath)) )
			{
				MessageBox.Show("You do not have the Windows XP Embedded tools installed.  You must install the tools to be able to compile DUA script files.", "Error - tools not installed", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			// Make sure that the output directory exists
			Directory.CreateDirectory(DUA_OutputFolder);

			// Launch dusc.exe if we get this far
			Process processDusc = new Process();
			processDusc.StartInfo.UseShellExecute = false;
			processDusc.StartInfo.RedirectStandardOutput = true;
			processDusc.StartInfo.FileName = strDuscPath;
			processDusc.StartInfo.Arguments = "\""+strSourceFile+"\" \""+g_strSaveFile+"\"";
			processDusc.Start();

			// Wait for the dusc process to finish
			processDusc.WaitForExit();

			// Retrieve the stdout information and parse it to determine
			// success/failure of the compilation
			string strDuscOutput = processDusc.StandardOutput.ReadToEnd().Trim();

			// Check the first line to see if it contains the success string
			if ( (null != strDuscOutput) && (-1 != strDuscOutput.IndexOf("Successfully created")) )
			{
				MessageBox.Show("The DUA script compiler has successfully compiled " + g_strSaveFile, "DUAScriptGen compiler", MessageBoxButtons.OK, MessageBoxIcon.Information);
			}
			else
			{
				MessageBox.Show("The DUA script compiler failed due to the following error:\n\n'" + strDuscOutput + "'", "DUAScriptGen compiler", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void menuItem2_Click(object sender, System.EventArgs e)
		{
            MessageBox.Show("DUAScriptGen " + g_strVersionString + "\n\nMicrosoft Windows XP Embedded Power Toy\n(C) Microsoft Corporation\n\nFor support see the following web site:\n\nhttp://www.codeplex.com/duascriptgen/", "DUAScriptGen v1.1");
		}

		private void listView1_DoubleClick(object sender, System.EventArgs e)
		{
			ListView.SelectedIndexCollection SelectedIndexes=listView1.SelectedIndices;
			int iCount=SelectedIndexes.Count;

			if (iCount == 1) 
			{
				GoEditItem(SelectedIndexes[0]);
			} 
		}

		
		/*****************************************************************************************
		Function Name:	listView2_DoubleClick
		Author:			aaronste
		Description:	Handler for the registry list view double-click action
		Inputs:			object sender - calling object
						System.EventArgs e - container for additional arguments for this event
		Outputs:		none
		******************************************************************************************/
		private void listView2_DoubleClick(object sender, System.EventArgs e)
		{
			ListView.SelectedIndexCollection SelectedIndexes = listView2.SelectedIndices;
			int iCount = SelectedIndexes.Count;

			if (1 == iCount) 
			{
				GoRegistryEditItem(SelectedIndexes[0]);
			} 
		}

		
		/*****************************************************************************************
		Function Name:	EditRegistryItem_Click
		Author:			aaronste
		Description:	Handler for the registry edit button click action
		Inputs:			object sender - calling object
						System.EventArgs e - container for additional arguments for this event
		Outputs:		none
		******************************************************************************************/
		private void EditRegistryItem_Click(object sender, System.EventArgs e)
		{
			ListView.SelectedIndexCollection SelectedIndexes = listView2.SelectedIndices;
			int iCount = SelectedIndexes.Count;

			if (1 == iCount)
			{
				GoRegistryEditItem(SelectedIndexes[0]);
			} 
			else 
			{
				MessageBox.Show("Please select exactly one registry item to edit");
			}
		}


		/*****************************************************************************************
		Function Name:	GoRegistryEditItem
		Author:			aaronste
		Description:	Creates and shows a registry value edit dialog and then updates the main
						UI with changes made by the user in the edit dialog
		Inputs:			int iSelectedItem - index into the registry item list to manipulate
		Outputs:		none
		******************************************************************************************/
		void GoRegistryEditItem(int iSelectedItem)
		{
			// Create the registry value edit dialog and set the initial data
			Registry_EditItem eItem = new Registry_EditItem();
			eItem.m_dRegEntry = (DUARegistryEntry)listView2.Items[iSelectedItem].Tag;

			// Show the registry value edit dialog
			eItem.ShowDialog();

			// Update the data and UI if the user pressed OK on the dialog
			if (DialogResult.OK == eItem.DialogResult) 
			{
				listView2.Items[iSelectedItem].Tag = eItem.m_dRegEntry;

				// Update reg root, reg key name, and DUA action based on the user selections
				listView2.Items[iSelectedItem].Text = g_strRegRoots[eItem.m_dRegEntry.m_iRegRoot];
				listView2.Items[iSelectedItem].SubItems[1].Text = eItem.m_dRegEntry.m_strRegKeyName;
				listView2.Items[iSelectedItem].SubItems[3].Text = g_strRegistryOperations[eItem.m_dRegEntry.m_iRegistryOperation];

				// Need to check for blank strings and format the registry value appropriately
				if ("" != eItem.m_dRegEntry.m_strRegValueName)
				{
					listView2.Items[iSelectedItem].SubItems[2].Text = eItem.m_dRegEntry.m_strRegValueName;
				
					if ("" != eItem.m_dRegEntry.m_strRegValueData)
						listView2.Items[iSelectedItem].SubItems[2].Text += "=" + eItem.m_dRegEntry.m_strRegValueData;
				}
				else
					listView2.Items[iSelectedItem].SubItems[2].Text = "";

			}
		}

		/*****************************************************************************************
		Function Name:	RemoveRegKeys_Click
		Author:			aaronste
		Description:	Handler for the registry remove button click action
		Inputs:			object sender - calling object
						System.EventArgs e - container for additional arguments for this event
		Outputs:		none
		******************************************************************************************/
		private void RemoveRegKeys_Click(object sender, System.EventArgs e)
		{
			ListView.SelectedIndexCollection SelectedIndexes = listView2.SelectedIndices;
			int iCount = SelectedIndexes.Count;

			// If at least one item is selected, remove it from the bottom up
			if (iCount > 0) 
			{
				for(int x = iCount-1; x > -1; x--)
				{
					listView2.Items.RemoveAt(SelectedIndexes[x]);
				}
			}
		}

		private void listView1_SelectedIndexChanged(object sender, System.EventArgs e)
		{
			ListView.SelectedIndexCollection SelectedIndexes = listView1.SelectedIndices;
			int iCount = SelectedIndexes.Count;

			if (0 == iCount) 
			{
				// If no items are selected, disable the remove button and the edit button
				button1.Enabled = false;
				EditItem.Enabled = false;
			}
			else if (1 == iCount)
			{
				// If exactly one item is selected, enable the remove and the edit buttons
				button1.Enabled = true;
				EditItem.Enabled = true;
			}
			else if (1 < iCount)
			{
				// If more than one item is selected, enable the remove button and disable the edit button
				button1.Enabled = true;
				EditItem.Enabled = false;
			}
		}

		private void listView2_SelectedIndexChanged(object sender, System.EventArgs e)
		{
			ListView.SelectedIndexCollection SelectedIndexes = listView2.SelectedIndices;
			int iCount = SelectedIndexes.Count;

			if (0 == iCount) 
			{
				// If no items are selected, disable the remove button and the edit button
				RemoveRegistryKey.Enabled = false;
				EditRegistryKey.Enabled = false;
			}
			else if (1 == iCount)
			{
				// If exactly one item is selected, enable the remove and the edit buttons
				RemoveRegistryKey.Enabled = true;
				EditRegistryKey.Enabled = true;
			}
			else if (1 < iCount)
			{
				// If more than one item is selected, enable the remove button and disable the edit button
				RemoveRegistryKey.Enabled = true;
				EditRegistryKey.Enabled = false;
			}
		}

		private void ConvertQFEFile()
		{
			// Set the file open dialog filter
			openFileDialog1.FileName = "";
			openFileDialog1.DefaultExt = "rtf";
			openFileDialog1.Filter = "Rich text files (*.rtf)|*.rtf|HTML files (*.htm;*.html)|*.htm;*.html|Text files (*.txt)|*.txt|All files (*.*)|*.*";
			
			// Show the file open dialog
			DialogResult result = openFileDialog1.ShowDialog();
			
			// Parse the file if the OK button was pressed
			if (result == DialogResult.OK) 
			{
				// Check the file extension and call the appropriate parsing method
				if ( (openFileDialog1.FileName.ToLower().EndsWith(".htm")) || (openFileDialog1.FileName.ToLower().EndsWith(".html")) )
				{
					ParseQFEHtmlFile(openFileDialog1.FileName);
				}
				else
				{
					// If the file is not named *.htm, then treat it as an RTF
					// file and parse it accordingly
					ParseQFEFile(openFileDialog1.FileName);
				}
			}
		}

		private void ConvertInfFile()
		{
			// Set the file open dialog filter
			openFileDialog1.FileName = "";
			openFileDialog1.DefaultExt = "inf";
			openFileDialog1.Filter = "INF files (*.inf)|*.inf|All files (*.*)|*.*";
			
			// Show the file open dialog
			DialogResult result = openFileDialog1.ShowDialog();
			
			// Parse the file if the OK button was pressed
			if (result == DialogResult.OK) 
			{
				// Call the INF parsing method
				ParseQFEInfFile(openFileDialog1.FileName);
			}
		}

		private void btnParseRTF_Click(object sender, System.EventArgs e)
		{
			ConvertQFEFile();
		}

		private void menuClearAllFiles_Click(object sender, System.EventArgs e)
		{
			ClearAllFileEntries();
		}

		private void menuClearAllRegistry_Click(object sender, System.EventArgs e)
		{
			ClearAllRegistryEntries();
		}

		private void menuClearAll_Click(object sender, System.EventArgs e)
		{
			ClearAllFileEntries();
			ClearAllRegistryEntries();
		}

		private void ClearAllFileEntries()
		{
			int i;

			// Remove all entries from the file list view
			for (i = listView1.Items.Count - 1; i > -1; i--)
				listView1.Items.Remove(listView1.Items[i]);

			// Disable the remove/edit buttons in the UI
			button1.Enabled = false;
			EditItem.Enabled = false;
		}

		private void ClearAllRegistryEntries()
		{
			int i;

			// Remove all entries from the registry list view
			for (i = listView2.Items.Count - 1; i > -1; i--)
				listView2.Items.Remove(listView2.Items[i]);

			// Disable the remove/edit buttons in the UI
			RemoveRegistryKey.Enabled = false;
			EditRegistryKey.Enabled = false;
		}

		private void menuLoadDUSFile_Click(object sender, System.EventArgs e)
		{
			openFileDialog1.FileName = "";
			openFileDialog1.DefaultExt = "dus";
			openFileDialog1.Filter = "Device update script file (*.dus)|*.dus|All files (*.*)|*.*";
			
			DialogResult result = openFileDialog1.ShowDialog();
			
			// Try to parse the file if the OK button was pressed
			if (result == DialogResult.OK) 
			{
				ParseDusFile(openFileDialog1.FileName);

				// Update the name of the DUP file to generate
				g_strSavePath = openFileDialog1.FileName;
				int iIndex = g_strSavePath.LastIndexOf("\\");
				if (-1 != iIndex) 
				{
					strSourceFile = openFileDialog1.FileName;
					g_strSavePath = g_strSavePath.Substring(0,iIndex);
					DUA_OutputFolder = g_strSavePath + "\\DUA_Files";

					g_strSaveFile = openFileDialog1.FileName.Substring(iIndex+1);
					iIndex = g_strSaveFile.LastIndexOf(".");
					g_strSaveFile = g_strSaveFile.Substring(0,iIndex);
					g_strSaveFile = DUA_OutputFolder + "\\" + g_strSaveFile + ".dup";
				}
			}
		}


		/*****************************************************************************************
		Function Name:	IsRegistryDataSizeParameterRequired
		Author:			aaronste
		Description:	Determines whether or not a size value is required for a DUS entry for
						the passed in registry value data type
		Inputs:			iRegValueDataType - integer value of the regvaluedata type indexed from
						the E_REGVALUEDATATYPE enumeration
		Outputs:		true if a size parameter is required, false otherwise
		******************************************************************************************/
		private bool IsRegistryDataSizeParameterRequired(int iRegValueDataType)
		{
			bool bRetValue = false;

			// Only binary, link, multi_sz and none data types require a size value
			switch (iRegValueDataType)
			{
				case ((int)E_REGVALUEDATATYPE.REG_BINARY):
					bRetValue = true;
					break;
				
				case ((int)E_REGVALUEDATATYPE.REG_LINK):
					bRetValue = true;
					break;
				
				case ((int)E_REGVALUEDATATYPE.REG_MULTI_SZ):
					bRetValue = true;
					break;
				
				case ((int)E_REGVALUEDATATYPE.REG_NONE):
					bRetValue = true;
					break;
				
				default:
					bRetValue = false;
					break;
			}

			return bRetValue;
		}
	

		/*****************************************************************************************
		Function Name:	ConvertHexToDecimal
		Author:			aaronste
		Description:	Takes a comma-delimited registry value data string and converts the
						items from hex to decimal values
		Inputs:			strInputData - input registry value data string
		Outputs:		registry value data string with individual items in decimal instead of hex
		******************************************************************************************/
		private string ConvertHexToDecimal(string strInputData)
		{
			// Check input string
			if ( (null == strInputData) || ("" == strInputData) )
				return strInputData;

			string strOutputData = "";
			string [] strInputTokens;
			string strConvertedToken = "";

			// Tokenize the input string using commas as delimiters
			strInputTokens = strInputData.Split(',');

			// If there are not at least 2 tokens, set the output equal to the input
			if (strInputTokens.Length <= 1)
			{
				strOutputData = strInputData;
			}
			else
			{
				// Loop through the tokens and convert them to decimal and add them to
				// the output string
				foreach (string strToken in strInputTokens)
				{
					// Add a comma to the string if it is not the first token
					if ("" != strOutputData)
						strOutputData += ",";

					// Convert the value to hex and then to decimal
					strConvertedToken = (Convert.ToInt16(strToken, 16)).ToString();

					// Add a leading 0 if appropriate
					if (1 == strConvertedToken.Length)
						strConvertedToken = "0" + strConvertedToken;

					// Append the new value to the output string
					strOutputData += strConvertedToken;
				}
			}

			return strOutputData;
		}


		/*****************************************************************************************
		Function Name:	SubstituteForTokens
		Author:			aaronste
		Description:	Takes a string and replaces a set of known tokens
						from a set of global variables with updated values
		Inputs:			strInputString - input string to replace tokens in
		Outputs:		string with tokens replaced by updated values
		******************************************************************************************/
		private string SubstituteForTokens(string strInputString)
		{
			// Check input string
			if ( (null == strInputString) || ("" == strInputString) )
				return strInputString;

			// Make sure the global variable for local drive location
			// is set to non-blank - there should not be a way to get
			// here if it is blank but this is a failsafe
			if ( (null == g_strLocalDrive) || ("" == g_strLocalDrive) )
				return strInputString;

			string strOutputString = "";
			string strLocalDriveLetter = "";
			int iIndex;

			// Get the local drive letter from the local drive global variable
			iIndex = g_strLocalDrive.IndexOf(":");
			if (-1 == iIndex)
				return strInputString;

			strLocalDriveLetter = g_strLocalDrive.Substring(0, iIndex+1);

			// Copy the input string into the output string because we want
			// to modify the output string in the loop below
			strOutputString = strInputString;

			// Loop through the list of tokens and replace any that exist
			// in the passed-in string
			for (int i = 0; i < g_strFolderTokens.Length; i++)
			{
				strOutputString = strOutputString.Replace(g_strFolderTokens[i], strLocalDriveLetter + g_strFolderTokenReplacements[i]);
			}

			return strOutputString;
		}

		private void menuConvertQFE_Click(object sender, System.EventArgs e)
		{
			ConvertQFEFile();
		}

		private void btnParseInf_Click(object sender, System.EventArgs e)
		{
			ConvertInfFile();
		}

		private void menuConvertInf_Click(object sender, System.EventArgs e)
		{
			ConvertInfFile();
		}

	}

	public class DUAFile
	{
		public int		Operation;			// delete, copy, move, etc

		public string	SourceFile;
		public string	DestFile;
		public string	DestLocation;
		public string	ExecuteCommandLine;

		public bool		bCustomLocation;	// Not one of the pre-defined locations
		public bool		bCreateCustomLocation; // Whether or not to create the custom folder first

		public bool     bRebootRequired;  // Whether or not this file will be in use on the target system

		public DUAFile()
		{
			// Set default values for this file entry
			Operation = (int) E_FILEOPERATION.Move;

			SourceFile = "";
			DestFile = "";
			DestLocation = "\\windows";
			ExecuteCommandLine = "";

			bCustomLocation = false;
			bCreateCustomLocation = false;

			bRebootRequired = false;
		}
	}

	public class DUARegistryEntry
	{
		public string	m_strRegKeyName;
		public string	m_strRegValueName;
		public string	m_strRegValueData;

		public int		m_iRegRoot;
		public int		m_iRegValueDataType;
		public int		m_iRegistryOperation;

		public DUARegistryEntry()
		{
			// Set default values for this registry entry
			m_strRegKeyName = "";
			m_strRegValueName = "";
			m_strRegValueData = "";

			m_iRegRoot = (int) E_REGISTRYROOT.HKEY_LOCAL_MACHINE;
			m_iRegistryOperation = (int) E_REGISTRYOPERATION.CreateAndAddValue;
			m_iRegValueDataType = (int) E_REGVALUEDATATYPE.REG_SZ;

		}
	}
}


/*
 * 
 * HTTPGET,0,0,servername,0,images/myimage.jpg,0,c:\dua\images\myimage.jpg,0,3,0,PROXYNAME,0,<local>,0,80,0,HTTP/1.1,0,ANY,0,0,UserName,0,PassWord,0,3,image
 * COPYFILE,,,c:\dua_temp\dua-log.txt,,c:\dua_temp\child\dua-log.txt,DANO
 * REGCREATEKEY,, HKEY_LOCAL_MACHINE ,, SYSTEM\CurrentControlSet\SampleKey , DAREG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS
 * REGSETVALUE,,HKEY_LOCAL_MACHINE,,SYSTEM\CurrentControlSet\SampleKey,,SampleValue,DAREG_SZ,,Hello World
 * */
